diff --git a/.dockerignore b/.dockerignore
index efffb918c9ccf917a14a836581e96540fc25cde1..13fb47336838dfbe939286133bd4394420129cd6 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1 @@
-# exclude everything
-*
-
-# include binary distribution archives
-!build/distributions/
+# exclude nothing
diff --git a/.gitignore b/.gitignore
index 62d8458021b782ac52ac9deba0f0660cf2e04294..7c909f48e7cac1c703b044139e7bb9cfe9a676c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,7 @@
 .secret
 
 #logging
-crymlin.log
-medina-codyze.log
+*.log
 
 # Mobile Tools for Java (J2ME)
 .mtj.tmp/
diff --git a/Dockerfile b/Dockerfile
index d9ead257c01564dbba0590a8fc2162757cf577c5..5cfe4fc4f6d3bef5c2570ca5e2a79d09e3f7c0af 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,23 +1,44 @@
-FROM openjdk:11-jre-slim
+# Builder
+FROM eclipse-temurin:11.0.20.1_1-jdk AS builder
 
+# required for Codyze MEDINA functionalities -> git, gpg
 RUN apt-get update && apt-get -y --no-install-recommends install \
-    unzip \
-    wget
+    git \
+    gpg \
+  && rm -rf /var/lib/apt/lists/*
 
-# add distribution
-ADD build/distributions/codyze-*.tar /usr/local/lib/
-# add links for ease of use
-RUN ln -s /usr/local/lib/codyze-*/bin/codyze /usr/local/bin/ \
-    && ln -s /usr/local/lib/codyze-* /codyze
+WORKDIR /codyze-medina
 
-RUN wget "https://github.com/Fraunhofer-AISEC/codyze/archive/refs/heads/main.zip" \
-    && unzip main.zip \
-    && mv codyze-main/src/dist/mark/ /codyze/mark/ \
-    && rm -rf main.zip codyze-main
+# files for build filtered by .dockerignore
+ADD . .
+RUN ./gradlew --parallel \
+        generateAll \
+    && ./gradlew --parallel \
+        build \
+        installDist
 
-# working location with sources to be analyzed
-WORKDIR /source
+
+# Executable image
+FROM eclipse-temurin:11.0.20.1_1-jre
+
+LABEL org.opencontainers.image.authors="Fraunhofer AISEC <codyze@aisec.fraunhofer.de>"
+LABEL org.opencontainers.image.vendor="Fraunhofer AISEC"
+LABEL org.opencontainers.image.licenses="Apache-2.0"
+LABEL org.opencontainers.image.title="Codyze for MEDINA"
+LABEL org.opencontainers.image.description="Compliance checks for source code using Codyze. Adjusted for MEDINA framework."
+
+COPY --from=builder /codyze-medina/build/install/ /
+RUN ln -st /usr/local/bin/ /codyze-medina/bin/codyze-medina
+
+# required for Codyze MEDINA functionalities -> git, gpg
+RUN apt-get update && apt-get -y --no-install-recommends install \
+    git \
+    gpg \
+  && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /project
+# Add workdir to safe directories
+RUN git config --global --add safe.directory /project
 
 # default entrypoint and parameters
-ENTRYPOINT ["codyze"]
-CMD ["-c"]
+ENTRYPOINT ["codyze-medina"]
\ No newline at end of file
diff --git a/README.md b/README.md
index be5a7b429fa802f621fda28238f55de2fc8b2677..dfddf9315fff54b3b98b381293162566b02db5b6 100644
--- a/README.md
+++ b/README.md
@@ -48,14 +48,22 @@ This makes Codyze for MEDINA available for immediate use in `./build/install/cod
 
 Codyze for MEDINA provides a CLI. The main options are:
 
-| Option                | Description                                                 |
-|-----------------------|-------------------------------------------------------------|
-| `--endpoint <URL>`    | URL of Orchestrator endpoint                                |
-| `--oauth-endpoint <URL>` | URL of OAuth endpoint                                       |
-| `--username <STRING> \| -u <STRING>`                                                | username for OAuth |
-| `--password <STRING> \| -p <STRING>`                                                | password for OAuth |
-| `--ci <STRING>` | CI environment being used (i.e., [GITLAB, GITHUB, JENKINS]) |
-| `--config <FILE>`     | configuration file for Codyze                               |
+| Option                              |          Default           | Description                                                                                                                                   |
+|-------------------------------------|:--------------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------|
+| `--id <STRING>`                     |             -              | UUID of the analyzed cloud service                                                                                                            |
+| `--rules <FILE>`                    | codyze-medina-metrics.yaml | File specifying environmental MEDINA rules                                                                                                    |
+| `--medina-output <PATH>`            |    codyze-medina.sarif     | File where MEDINA rule evaluations will be stored. In case `combined-output` is set to true, this is the location of the combined result file |
+| `--combined-output <BOOLEAN>`       |            true            | Whether the respective SARIF files created by Codyze and the MEDINA evaluation should be merged                                               |
+| `--key-location <PATH>`             |        public-keys/        | Location of public key files necessary to evaluate signatures                                                                                 |
+| `--endpoint <URL>`                  |             -              | URL of Orchestrator endpoint                                                                                                                  |
+| `--oauth-endpoint <URL>`            |             -              | URL of OAuth endpoint                                                                                                                         |
+| `--username <STRING> / -u <STRING>` |             -              | username for OAuth                                                                                                                            |
+| `--password <STRING> / -p <STRING>` |             -              | password for OAuth                                                                                                                            |
+| `--required <BOOLEAN>`              |            true            | Whether analysis fails on Orchestrator connection issues.                                                                                     |
+| `--mark-builtin <LIST<FILE>>`       |             []             | Builtin MARK files used in the analysis                                                                                                       |
+| `--mark-project <LIST<FILE>>`       |             []             | External MARK files used in the analysis                                                                                                      |
+| `--ci <STRING>`                     |            NONE            | CI environment being used (i.e., [GITLAB, GITHUB, JENKINS]). If set to `NONE`, The program will try to determine this at runtime.             |
+| `--config <FILE>`                   |     codyze-medina.yaml     | Configuration file for Codyze                                                                                                                 |
 
 In addition, Codyze for MEDINA passes CLI options along to Codyze v2. Options for Codyze v2 can be found in the
 documentation at [codyze.io](shttps://www.codyze.io/Getting%20Started/configuration/).
@@ -71,24 +79,87 @@ The structure of a configuration file is:
 
 ```yaml
 orchestrator:
+  required: <BOOLEAN>
   endpoint: <URL>
   auth:
     oauth-endpoint: <URL>
     username: <STRING>
     password: <STRING>
-
+    
+mark:
+  builtin:
+    - <PATH>
+    - ...
+  project:
+    - <PATH>
+    - ...
+
+id: <STRING>
+rules: <FILE>
+
+# ... additional optional parameters ...
 # ... additional parameters from original Codyze ...
 ```
 
+The MARK file paths are interpreted based on the location of the codyze distribution:
+- The `builtin` segment represents included MARK rule groups found in `codyze-medina/mark`. Expected inputs are the names of the respective subdirectories.
+- The `project` segment represents additional MARK rules provided by any other source. Note that they also need to include a valid mapping file to be considered during the analysis.
+
 See the test resources for an example configuration file. It uses a locally running Orchestrator.
 
+The configuration file should be located within the target project to comply with the following assumptions:
+ - All **relative paths** used in the configuration of Codyze for MEDINA are evaluated relative to the location of the configuration file.
+ - The specified MEDINA Metrics from the `rules` parameter are evaluated at the location of the configuration file.
+
 ## Mapping
 
 Mapping from `Finding`s to `AssessmentResult`s is currently handled by the mapping file
-`src/main/java/resoures/mappings.txt`. Its entries follow the
-scheme `[findingId0:findingId1:...]->(metricId;isDefault;operator;targetValue;valueType)`
-where `;` separates different parameters and `:` separates elements of a list. See the mapping file in this project for
-an example.
+`mappings.txt`. 
+Its entries follow this scheme:
+```
+metrics:
+  - name: <Metric Name>
+    rules:
+      - <Required MARK Rule>
+      - <Required MARK Rule>
+      - ...
+    configuration:
+        default: <Whether Config is Default>
+        operator: <Comparator as String>
+        type: [STRING, NUMBER, BOOLEAN]
+        target:
+          - <Target Value>
+          - ...
+  - ...
+```
+
+All findings NOT mapped in such a file are ignored during the analysis.
+> More information about the mapping file can be found in `docs/mapping.md`
+
+## Exit Codes
+
+Codyze for MEDINA currently returns one of the following exit codes depending on the situation:
+
+| Exit Code | Meaning                                                 |
+|-----------|---------------------------------------------------------|
+| **126**   | Orchestrator Connection Failed                          |
+| **3**     | Unrecognized Cloud Service Id                           |
+| **127**   | General Execution Error (consult logs for more details) |
+| **1**     | Violations found during analysis                        |
+| **0**     | No violations or errors                                 |
+
+Exit codes higher up in this list take priority over lower ones, e.g. a return code of 126 does not exclude violations within the analysis results.
+
+## Environment Variables
+
+The following environment variables can be used to additionally configure the execution:
+
+| Name             | Effect                                                                                                              |
+|------------------|---------------------------------------------------------------------------------------------------------------------|
+| CODYZE_PWD       | Sets the orchestrator password. Overwrites configuration file entries but is overwritten by command line arguments. |
+| CODYZE_LOG_LEVEL | Manually overrides the log level                                                                                    |
+
+Additionally, Codyze for MEDINA parses variables automatically set by the CI system to gain necessary information about the environment.
 
-All findings NOT mapped in this file are ignored when sending results to the orchestrator (they will still be present in
-the result file).
\ No newline at end of file
+> Codyze for MEDINA requires the target project to be within a Git repository.
+> Additionally, GnuPG must be available in order to evaluate any signatures.
diff --git a/build.gradle.kts b/build.gradle.kts
index 578492285eddc10fba4ee668f2bc25a91a8bfbd3..84f7c97c6551947ef40fb3fc459fec23adee459c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,22 +1,22 @@
 import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
 
 plugins {
-    java
-    kotlin("jvm") version "1.7.20"
+    kotlin("jvm") version "1.9.20"
     application
 
-    id("org.openapi.generator") version "6.2.0"
+    // generator for OpenAPI specs
+    id("org.openapi.generator") version "7.1.0"
 
     // code quality
     jacoco
-    id("com.diffplug.spotless") version "6.11.0"
+    id("com.diffplug.spotless") version "6.22.0"
 
     // documentation
-    id("org.jetbrains.dokka") version "1.7.20"
+    id("org.jetbrains.dokka") version "1.9.10"
 }
 
 group = "de.fraunhofer.aisec.codyze.medina"
-version = "1.0.0"
+version = "1.6.0"
 
 repositories {
     // JitPack for packages build directly from GitHub repos
@@ -50,53 +50,55 @@ repositories {
 }
 
 dependencies {
-    // Java only
-    testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.1")
-    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1")
+    // testing
+    testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
+    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.1")
+    testImplementation("org.mockito:mockito-junit-jupiter:5.7.0")
 
     // depend on executing the build.gradle of the generated openapi-projects
     api(project("generator_orchestrator"))
     api(project("generator_evidence"))
 
-    // codyze
-    api("com.github.Fraunhofer-AISEC:codyze:2.2.0")
+    // Codyze
+    implementation("com.github.Fraunhofer-AISEC:codyze:2.3.0")
     implementation(
         "com.github.Fraunhofer-AISEC.codyze-mark-eclipse-plugin:de.fraunhofer.aisec.mark:2.0.0:repackaged"
     )
 
     // CLI using picocli
-    implementation("info.picocli:picocli:4.6.3")
-    annotationProcessor("info.picocli:picocli-codegen:4.6.3")
+    implementation("info.picocli:picocli:4.7.5")
+    annotationProcessor("info.picocli:picocli-codegen:4.7.5")
 
     // OAuth2
-    api("org.dmfs:oauth2-essentials:0.18")
-    implementation("org.dmfs:httpurlconnection-executor:0.20")
+    implementation("org.dmfs:oauth2-essentials:0.22.1")
+    implementation("org.dmfs:httpurlconnection-executor:1.22.1")
 
-    // Logging
-    implementation("io.github.microutils:kotlin-logging-jvm:3.0.2")
-    implementation("org.slf4j:slf4j-simple:2.0.3")
+    // logging
+    implementation("io.github.oshai:kotlin-logging-jvm:5.1.0")
+    implementation("org.slf4j:slf4j-api:2.0.9") // required by `io.github.oshai:kotlin-logging-jvm`
+    implementation("org.apache.logging.log4j:log4j-core:2.21.1")
+    runtimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl:2.21.1")
 
-    // additional dependencies
-    implementation("com.fasterxml.jackson.core:jackson-databind:2.13.4.2")
-    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.4")
+    // YAML configuration files
+    implementation("com.fasterxml.jackson.core:jackson-databind:2.16.0")
+    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.0")
 
-    // kotlin
-    implementation(kotlin("stdlib-jdk8"))
+    // SARIF Kotlin bindings
+    implementation("io.github.detekt.sarif4k:sarif4k:0.5.0")
 }
 
-java {
-    toolchain {
-        languageVersion.set(JavaLanguageVersion.of(11))
-    }
+kotlin {
+    jvmToolchain(11)
 }
 
 application {
-    mainClass.set("de.fraunhofer.aisec.codyze.medina.CodyzeMedinaKt")
+    mainClass.set("de.fraunhofer.aisec.codyze.medina.main.CodyzeMedinaKt")
 }
 
 distributions {
     main {
         contents {
+            // copy over important files
             from("mark/") {
                 into("mark/")
             }
@@ -109,50 +111,62 @@ distributions {
 // license header used with spotless
 val license =
     """
-        /*
+        // SPDX-License-Identifier: Apache-2.0
+        
+        /* 
          * Copyright (c) ${"$"}YEAR, Fraunhofer AISEC. All rights reserved.
-         *
+         * 
          * Licensed under the Apache License, Version 2.0 (the "License");
          * you may not use this file except in compliance with the License.
          * You may obtain a copy of the License at
-         *
+         * 
          *      https://www.apache.org/licenses/LICENSE-2.0
-         *
+         * 
          * Unless required by applicable law or agreed to in writing, software
          * distributed under the License is distributed on an "AS IS" BASIS,
          * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          * See the License for the specific language governing permissions and
          * limitations under the License.
-         *     _____          _               
-         *    / ____|        | |              
-         *   | |     ___   __| |_   _ _______ 
+         *
+         *     _____          _
+         *    / ____|        | | 
+         *   | |     ___   __| |_   _ _______
          *   | |    / _ \ / _` | | | |_  / _ \
          *   | |___| (_) | (_| | |_| |/ /  __/
          *    \_____\___/ \__,_|\__, /___\___|
-         *                      __/ |        
-         *                     |___/         
+         *                      __/ |
+         *                     |___/
+         * 
+         * This file is part of the MEDINA Framework.
          */
     """.trimIndent()
 
 spotless {
-    ratchetFrom("origin/main")
-    java {
-        importOrder()
-        removeUnusedImports()
-        googleJavaFormat()
-        licenseHeader(license).yearSeparator("-")
-    }
     kotlin {
         ktfmt().kotlinlangStyle()
         licenseHeader(license).yearSeparator("-")
     }
 }
 
+// Manually exclude all spotless-Tasks of the "generator_"-subprojects from the gradle task graph.
+// This is a workaround to prevent the exceptions thrown from the outdated google-java-format specified there
+gradle.taskGraph.whenReady {
+    if(hasTask(":spotlessJava") || hasTask(":spotlessJavaDiagnose")) {
+        allTasks.forEach {
+            for (project in subprojects) {
+                if(it.path.matches(":generator_.+:spotless.*".toRegex())) {
+                    it.enabled = false
+                }
+            }
+        }
+    }
+}
+
 // generates an openapi-project from the `orchestrator.yaml` specification in the resources
 // directory
 tasks.register<GenerateTask>("generateOrchestrator") {
     inputSpec.set("$rootDir/src/main/resources/orchestrator.yaml")
-    outputDir.set("$buildDir/generator_orchestrator")
+    outputDir.set("${layout.buildDirectory.get().asFile}/generator_orchestrator")
     generatorName.set("java")
     apiPackage.set("org.openapitools.client.orchestrator.api")
     modelPackage.set("org.openapitools.client.orchestrator.model")
@@ -162,7 +176,7 @@ tasks.register<GenerateTask>("generateOrchestrator") {
 // generates an openapi-project from the `evidence.yaml` specification in the resources directory
 tasks.register<GenerateTask>("generateEvidence") {
     inputSpec.set("$rootDir/src/main/resources/evidence.yaml")
-    outputDir.set("$buildDir/generator_evidence")
+    outputDir.set("${layout.buildDirectory.get().asFile}/generator_evidence")
     generatorName.set("java")
     apiPackage.set("org.openapitools.client.evidence.api")
     modelPackage.set("org.openapitools.client.evidence.model")
@@ -175,7 +189,9 @@ tasks.register("generateAll") {
     dependsOn(tasks.named("generateEvidence"))
 }
 
-tasks.named<Test>("test") { useJUnitPlatform() }
+tasks.named<Test>("test") {
+    useJUnitPlatform()
+}
 
 tasks.test {
     finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
@@ -191,3 +207,19 @@ tasks.jacocoTestReport {
 tasks.dokkaHtml.configure {
     outputDirectory.set(rootDir.resolve("docs"))
 }
+
+tasks {
+    jar {
+        from("LICENSE") {
+            into("META-INF/")
+        }
+        // add name and version from gradle configuration to jar manifest
+        manifest {
+            attributes(
+                "Implementation-Title" to rootProject.name,
+                "Implementation-Version" to rootProject.version,
+                "Bundle-License" to "https://opensource.org/licenses/Apache-2.0"
+            )
+        }
+    }
+}
diff --git a/docs/mapping.md b/docs/mapping.md
new file mode 100644
index 0000000000000000000000000000000000000000..9d550b529787ac05358dfa323548556210096f7c
--- /dev/null
+++ b/docs/mapping.md
@@ -0,0 +1,67 @@
+# Mapping Documentation
+
+## Content of a single mapping
+
+To adapt the provided mapping, the following syntax must be considered:
+
+The mapping consists of a set of **metrics**, which define the results as they will be sent to the Orchestrator.
+
+Each metric is specified by its **name**, **rules** and **configuration**.
+
+### Rules
+
+It is possible to define one or multiple rules for each metric. These rules represent the MARK rules evaluated by Codyze.
+A metric will only be evaluated if _all_ of its rules have been evaluated prior. Only if _all_ of its rules passed the metric will be evaluated as compliant.
+
+### Configuration
+
+The configuration of a metric defines its evaluation in the Orchestrator.
+
+The **default** value indicates whether this configuration represents a default configuration or has been modified.
+
+The **operator** specifies the operator which is used to compare different results of this metric with the target value.
+
+The **type** specifies the data type the results of this metric have. Possible values are *BOOLEAN*, *NUMBER* and *STRING*.
+
+The **target** value specifies one or multiple values that define - together with the operator - which results are considered good.
+
+## Having multiple mappings
+
+There are many reasons why one would want to have multiple mapping files.
+One of them being that there may be rules with the same name in different modules which should not be verified together.
+
+Therefore, the specified Mark directory is being scanned top-to-bottom until a mapping is found.
+This mapping is subsequently applied to all hierarchical equivalent or lower rules within the same directory.
+Other mapping files found below an existing mapping will be ignored.
+
+When creating multiple mappings consider that every evaluated mapping will cause a separate analysis run.
+
+### Example
+
+Consider the following structure of the Mark rule source directory:
+
+```
+mark/
+├─ botan/
+│  ├─ botan-rules-1/
+│  │  ├─ mapping.yaml       [0]
+│  │  ├─ botan-rule-1.mark
+│  ├─ botan-rules-2/
+│  │  ├─ botan-rule-2.mark
+│  ├─ botan-rule-0.mark
+├─ bouncycastle/
+│  ├─ mapping.yaml          [1]
+│  ├─ bc-rules-1/
+│  │  ├─ bc-rule-1.mark
+│  ├─ bc-rules-2/
+│  │  ├─ bc-rule-2.mark
+│  │  ├─ mapping.yaml       [2]
+│  ├─ bc-rule-0.mark
+
+```
+
+In this scenario, `botan-rule-1.mark` will be included in the run evaluating `[0]`.
+All other rules in the `botan/` directory will be ignored as there is no mapping at or above their level.
+
+Within the `bouncycastle/` directory all rules will be analyzed in the context of `[1]`.
+The mapping found at `[2]` will not be applied as it is within a directory that already belongs to `[1]`.
\ No newline at end of file
diff --git a/docs/medina-rules.md b/docs/medina-rules.md
new file mode 100644
index 0000000000000000000000000000000000000000..68358b035ce95ffaa9f7fbf4208f1490ae6d8e84
--- /dev/null
+++ b/docs/medina-rules.md
@@ -0,0 +1,32 @@
+# MEDINA Rule Documentation
+
+## Content of a rule specification
+
+The file containing the MEDINA rules is specified by the program argument `--rules` and defaults to `medina.yaml`.
+The YAML content is expected to be structured in the following way:
+
+```
+metrics:
+  - name: <METRIC1>
+    target:
+      - <TARGET1>
+      - ...
+  - name: <METRIC2>
+    target:
+      - <TARGET1>
+      - ...
+  - ...
+```
+
+### Available rules
+
+The following table lists all currently available rules and the expected format for their respective target values:
+
+| Name          |                                                     Target Value                                                     | Explanation                                                                                 |
+|:--------------|:--------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------|
+| CodeSignoff   |                                                   "<SIGNER_NAME>"                                                    | Checks for a Signoff by the specified signer                                                |
+| SignedCommits |                                                  "<SIGNING_KEY_ID>"                                                  | Checks for a valid GPG signature from the specified key                                     |
+| SignedSignoff | <div>Map: {</br>name: "<SIGNER_NAME>", </br>email: "<SIGNER_EMAIL>", </br>pub-key-id: "<SIGNING_KEY_ID>"</br>}</div> | Checks whether the commit contains a valid signature and signoff from the specified subject |
+| ApprovedCommitAuthor | "<AUTHOR_NAME>"    | Checks whether the commit was authored from a specified subject |
+
+The `SIGNING_KEY_ID` is expected to be the 16-character-long ID of the key used for signing.
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 249e5832f090a2944b7473328c07c9755baa3196..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ae04661ee733431762e7ccf8ab9b7409ed44960c..3fa8f862f753336d4fabfd607678a7a2317e8a06 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index a69d9cb6c20655813e44515156e7253a2a239138..1aa94a4269074199e6ed2c37e8db3e0826030965 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +80,11 @@ do
     esac
 done
 
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
@@ -133,22 +131,29 @@ location of your Java installation."
     fi
 else
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
 if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
     case $MAX_FD in #(
       max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         MAX_FD=$( ulimit -H -n ) ||
             warn "Could not query maximum file descriptor limit"
     esac
     case $MAX_FD in  #(
       '' | soft) :;; #(
       *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         ulimit -n "$MAX_FD" ||
             warn "Could not set maximum file descriptor limit to $MAX_FD"
     esac
@@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
     done
 fi
 
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/gradlew.bat b/gradlew.bat
index 53a6b238d414d91c30c5644c82393d27416fbbe6..6689b85beecde676054c39c2408085f41e6be6dc 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
 
 set DIRNAME=%~dp0
 if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
diff --git a/local.Dockerfile b/local.Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..5e5fced13ec204160ff99e89d9811c342c785f14
--- /dev/null
+++ b/local.Dockerfile
@@ -0,0 +1,26 @@
+FROM eclipse-temurin:11.0.20.1_1-jre
+
+LABEL org.opencontainers.image.authors="Fraunhofer AISEC <codyze@aisec.fraunhofer.de>"
+LABEL org.opencontainers.image.vendor="Fraunhofer AISEC"
+LABEL org.opencontainers.image.licenses="Apache-2.0"
+LABEL org.opencontainers.image.title="Codyze for MEDINA"
+LABEL org.opencontainers.image.description="Compliance checks for source code using Codyze. Adjusted for MEDINA framework."
+
+# required for Codyze MEDINA functionalities -> git, gpg
+RUN apt-get update && apt-get -y --no-install-recommends install \
+    git \
+    gpg \
+  && rm -rf /var/lib/apt/lists/*
+
+# add distribution
+ADD build/distributions/codyze-*.tar /usr/local/lib/
+# add links for ease of use
+RUN ln -s /usr/local/lib/codyze-*/bin/codyze-medina /usr/local/bin/ \
+    && ln -s /usr/local/lib/codyze-* /codyze-medina
+
+WORKDIR /project
+# Add workdir to safe directories
+RUN git config --global --add safe.directory /project
+
+# default entrypoint
+ENTRYPOINT ["codyze-medina"]
\ No newline at end of file
diff --git a/mark/bc-jca/Cipher.mark b/mark/bc-jca/Cipher.mark
new file mode 100644
index 0000000000000000000000000000000000000000..fc6d829a25bfc7ca3f1e7dbc889c92cfc90891f6
--- /dev/null
+++ b/mark/bc-jca/Cipher.mark
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
+package java.jca
+
+entity Cipher {
+
+    var transform;
+    var provider;
+
+    var opmode;
+    var certificate;
+    var random;
+    var key;
+    var params;
+    var paramspec;
+
+    var input;
+    var output;
+
+    var wrappedkey;
+    var wrappedkeyalgorithm;
+    var wrappedkeytype;
+
+    op instantiate {
+        javax.crypto.Cipher.getInstance(
+            transform : java.lang.String
+        );
+        javax.crypto.Cipher.getInstance(
+            transform : java.lang.String,
+            provider : java.lang.String | java.security.Provider
+        );
+    }
+
+    op init {
+        javax.crypto.Cipher.init(
+            opmode : int,
+            certificate : java.security.cert.Certificate
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            certificate : java.security.cert.Certificate,
+            random : java.security.SecureRandom
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            params : java.security.AlgorithmParameters
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            params : java.security.AlgorithmParameters,
+            random : java.security.SecureRandom
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            random : java.security.SecureRandom
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            paramspec : java.security.spec.AlgorithmParameterSpec
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            paramspec : javax.crypto.spec.IvParameterSpec
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            paramspec : java.security.spec.AlgorithmParameterSpec,
+            random : java.security.SecureRandom
+        );
+        javax.crypto.Cipher.init(
+            opmode : int,
+            key : java.security.Key,
+            paramspec : javax.crypto.spec.IvParameterSpec,
+            random : java.security.SecureRandom
+        );
+    }
+
+    op aad {
+        javax.crypto.Cipher.updateAAD(src : byte[] | java.nio.ByteBuffer);
+        javax.crypto.Cipher.updateAAD(src: byte[], ...);
+    }
+
+    op update {
+        output = javax.crypto.Cipher.update(input : byte[]);
+        output = javax.crypto.Cipher.update(input : byte[], _, _);
+        javax.crypto.Cipher.update(input : byte[], _, _, output : byte[]);
+        javax.crypto.Cipher.update(input : byte[], _, _, output : byte[], _);
+        javax.crypto.Cipher.update(input : java.nio.ByteBuffer, output : java.nio.ByteBuffer);
+    }
+
+    op finalize {
+        output = javax.crypto.Cipher.doFinal();
+        output = javax.crypto.Cipher.doFinal(input : byte[]);
+        javax.crypto.Cipher.doFinal(output : byte[], _);
+        output = javax.crypto.Cipher.doFinal(input : byte[], _, _);
+        javax.crypto.Cipher.doFinal(input : byte[], _, _, output : byte[]);
+        javax.crypto.Cipher.doFinal(input : byte[], _, _, output : byte[], _);
+        javax.crypto.Cipher.doFinal(input : java.nio.ByteBuffer, output: java.nio.ByteBuffer);
+    }
+
+    op keywrap {
+        wrappedkey = javax.crypto.Cipher.wrap(key : java.security.Key);
+        key = javax.crypto.Cipher.unwrap(
+            wrappedkey : byte[],
+            wrappedkeyalgorithm : java.lang.String,
+            wrappedkeytype : int
+        );
+    }
+}
diff --git a/src/test/resources/Mark/bouncycastle/KeyAgreement.mark b/mark/bc-jca/KeyAgreement.mark
similarity index 61%
rename from src/test/resources/Mark/bouncycastle/KeyAgreement.mark
rename to mark/bc-jca/KeyAgreement.mark
index f2ed50a8fbf01ddb227cfdf23cc28464d538874f..c99e99cfaef7cc9e6c175e6f0df23252abb02467 100644
--- a/src/test/resources/Mark/bouncycastle/KeyAgreement.mark
+++ b/mark/bc-jca/KeyAgreement.mark
@@ -1,3 +1,29 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
 package java.jca
 
 entity KeyAgreement {
diff --git a/mark/bc-jca/LICENSE b/mark/bc-jca/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/mark/bc-jca/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/src/test/resources/Mark/bouncycastle/Mac.mark b/mark/bc-jca/Mac.mark
similarity index 52%
rename from src/test/resources/Mark/bouncycastle/Mac.mark
rename to mark/bc-jca/Mac.mark
index f0f90460b28d18376dbdc0e84b4b2a2b511a07c1..15c7933dbb8e596ceb10530d2478c339f7ab9e89 100644
--- a/src/test/resources/Mark/bouncycastle/Mac.mark
+++ b/mark/bc-jca/Mac.mark
@@ -1,20 +1,45 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
 package java.jca
 
 /*
  * Represents javax.crypto.Mac
  */
 entity Mac {
-    
+
     var algorithm;
     var provider;
-    
+
     var key;
     var params;
-    
+
     var input;
     var output;
     
-    
     op instantiate {
         javax.crypto.Mac.getInstance(
             algorithm : java.lang.String
@@ -24,7 +49,7 @@ entity Mac {
             provider : java.lang.String | java.security.Provider
         );
     }
-    
+
     op init {
         javax.crypto.Mac.init(key : java.security.Key);
         javax.crypto.Mac.init(
@@ -32,20 +57,19 @@ entity Mac {
             params : java.security.spec.AlgorithmParameterSpec
         );
     }
-    
+
     op update {
         javax.crypto.Mac.update(input : byte | byte[] | java.nio.ByteBuffer);
         javax.crypto.Mac.update(input : byte[], ...);
     }
-    
+
     op finalize {
         output = javax.crypto.Mac.doFinal();
         output = javax.crypto.Mac.doFinal(input : byte[]);
         javax.crypto.Mac.doFinal(output : byte[], _);
     }
-    
+
     op reset {
         javax.crypto.Mac.reset();
     }
-    
-}
\ No newline at end of file
+}
diff --git a/mark/bc-jca/MessageDigest.mark b/mark/bc-jca/MessageDigest.mark
new file mode 100644
index 0000000000000000000000000000000000000000..0e3b5d12cd4fad11165bde824d9592c30d1bb476
--- /dev/null
+++ b/mark/bc-jca/MessageDigest.mark
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
+package java.jca
+
+/*
+ * Represents java.security.MessageDigest
+ */
+entity MessageDigest {
+
+    var algorithm;
+    var provider;
+    var input;
+    var digest;
+
+    op instantiate {
+        java.security.MessageDigest.getInstance(algorithm : java.lang.String);
+        java.security.MessageDigest.getInstance(
+            algorithm : java.lang.String,
+            provider : java.lang.String | java.security.Provider
+        );
+    }
+
+    op update {
+        java.security.MessageDigest.update(input : byte | byte[] | java.nio.ByteBuffer);
+        java.security.MessageDigest.update(
+            input : byte[],
+            ...
+        );
+    }
+
+    op digest {
+        digest = java.security.MessageDigest.digest();
+        digest = java.security.MessageDigest.digest(input : byte[]);
+        java.security.MessageDigest.digest(digest : byte[], ...);
+    }
+
+    op reset {
+        java.security.MessageDigest.reset();
+    }
+}
diff --git a/mark/bc-jca/README.md b/mark/bc-jca/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1854e6c0daa21da08ec2fb2dfba69be85d874214
--- /dev/null
+++ b/mark/bc-jca/README.md
@@ -0,0 +1,8 @@
+# MARK for Bouncy Castle's JCA/JCE Provider
+
+## License
+These files are part of [Codyze](https://www.codyze.io/). 
+They are distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt).
+
+They have been included from [Codyze on GitHub](https://github.com/Fraunhofer-AISEC/codyze/) into Codyze for MEDINA. 
+Adjustments within the [MEDINA project](https://medina-project.eu/) are marked appropriately. 
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/SecureRandom.mark b/mark/bc-jca/SecureRandom.mark
similarity index 64%
rename from src/test/resources/Mark/bouncycastle/SecureRandom.mark
rename to mark/bc-jca/SecureRandom.mark
index fe531bf7501c140d46fbee81e275026bc4d02b5a..c85ea98f8548a2bf634215f4111990944053541e 100644
--- a/src/test/resources/Mark/bouncycastle/SecureRandom.mark
+++ b/mark/bc-jca/SecureRandom.mark
@@ -1,14 +1,40 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
 package java.jca
 
 entity SecureRandom {
-    
+
     var algorithm;
     var provider;
     var params;
     var seed;
     var numBytes;
     var randomBytes;
-    
+
     op instantiate {
         java.security.SecureRandom.getInstance(algorithm : java.lang.String);
         java.security.SecureRandom.getInstance(
@@ -32,20 +58,20 @@ entity SecureRandom {
             seed : byte[]
         );
     }
-    
+
     op seed {
         java.security.SecureRandom.setSeed(seed : byte[] | long);
     }
-    
+
     op reseed {
         java.security.SecureRandom.reseed();
         java.security.SecureRandom.reseed(params : java.security.SecureRandomParameters);
     }
-    
+
     op generateSeed {
         seed = java.security.SecureRandom.generateSeed(numBytes : int);
     }
-    
+
     op generate {
         java.security.SecureRandom.next(numBytes : int);
         java.security.SecureRandom.nextBytes(randomBytes : bytes[]);
@@ -54,5 +80,4 @@ entity SecureRandom {
             params : java.security.SecureRandomParameters
         );
     }
-    
-}
\ No newline at end of file
+}
diff --git a/mark/bc-jca/Signature.mark b/mark/bc-jca/Signature.mark
new file mode 100644
index 0000000000000000000000000000000000000000..61a6b8d42e38c3ba9a634fe7c3708fdbed1decb2
--- /dev/null
+++ b/mark/bc-jca/Signature.mark
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ */
+package java.jca
+
+entity Signature {
+
+    var algorithm;
+    var provider;
+
+    var privateKey;
+    var random;
+
+    var certificate;
+    var publicKey;
+
+    var b;
+    var data;
+    var off;
+    var len;
+
+    var outbuf;
+    var offset;
+    var len;
+
+    var signature;
+    var offset;
+    var length;
+
+    op instantiate {
+        java.security.Signature.getInstance(
+            algorithm : java.lang.String
+        );
+        java.security.Signature.getInstance(
+            algorithm : java.lang.String,
+            provider : java.lang.String | java.security.Provider
+        );
+    }
+
+    op initsign {
+        java.security.Signature.initSign(
+            privateKey : java.security.PrivateKey
+        );
+        java.security.Signature.initSign(
+            privateKey : java.security.PrivateKey,
+            random : java.security.SecureRandom
+        );
+    }
+
+    op initverify {
+        java.security.Signature.initVerify(
+            certificate : java.security.cert.Certificate
+        );
+        java.security.Signature.initVerify(
+            publicKey : java.security.PublicKey
+        );
+    }
+
+    op update {
+        java.security.Signature.update(
+            b : byte
+        );
+        java.security.Signature.update(
+            data : byte[] | java.nio.ByteBuffer
+        );
+        java.security.Signature.update(
+            data : byte[],
+            off : int,
+            len : int
+        );
+    }
+
+    op sign {
+        signature = java.security.Signature.sign();
+        java.security.Signature.sign(
+            outbuf : byte[],
+            offseet : int,
+            len : int
+        );
+    }
+
+    op verify {
+        java.security.Signature.verify(
+            signature : byte[]
+        );
+        java.security.Signature.verify(
+            signature : byte[],
+            offset : int,
+            length : int
+        );
+    }
+}
diff --git a/mark/bc-jca/findingDescription.json b/mark/bc-jca/findingDescription.json
new file mode 100644
index 0000000000000000000000000000000000000000..52dc0b7c80db3bb1b500ac1dd01614e803f08482
--- /dev/null
+++ b/mark/bc-jca/findingDescription.json
@@ -0,0 +1,92 @@
+{
+  "Ciphers": {
+    "fullDescription": {
+      "text": "Using insecure ciphers. Ensure the use of recommended ciphers by BSI TR-02102-1, which includes AES and RSA/ECIES/DLIES."
+    },
+    "shortDescription": {
+      "text": "Use of insecure ciphers."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use AES with cipher modes CCM, GCM, CTR, CBC or RSA and encryption schemes ECIES and DLIES."
+        }
+      }
+    ]
+  },
+  "HashFunctions": {
+    "fullDescription": {
+      "text": "Using insecure hash functions. Ensure the use of recommended hash functions by BSI TR-02102-1, which includes SHA-2 and SHA-2 of at least 256 bit digest size."
+    },
+    "shortDescription": {
+      "text": "Use of insecure hash functions."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use hash functions of the SHA-2 and SHA-3 family with at least 256 bit digest sizes."
+        }
+      }
+    ]
+  },
+  "MessageAuthenticationCodes": {
+    "fullDescription": {
+      "text": "Using insecure message authentication codes. Ensure the use of recommended message authentication codes by BSI TR-02102-1, which includes HMAC, CMAC and GMAC using recommended ciphers and hash functions."
+    },
+    "shortDescription": {
+      "text": "Use insecure message authentication codes."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use message authentication codes based on HMAC, CMAC and GMAC and using recommended ciphers and hash functions."
+        }
+      }
+    ]
+  },
+  "Signatures": {
+    "fullDescription": {
+      "text": "Using insecure signatures. Ensure the use of recommended signatures by BSI TR-02102-1, which includes DSA, ECDSA and RSA with recommended hash functions."
+    },
+    "shortDescription": {
+      "text": "Use of insecure signatures."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use signatures based on DSA, ECDSA and RSA with recommended hash functions."
+        }
+      }
+    ]
+  },
+  "SecureRandom": {
+    "fullDescription": {
+      "text": "Using insecure PRNG. Ensure the use of recommended PRNG by BSI TR-02102-1, which includes DRBG and blocking native PRNG."
+    },
+    "shortDescription": {
+      "text": "Use of insecure PRNG."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use PRNG based on DRBG or native blocking PRNG."
+        }
+      }
+    ]
+  },
+  "KeyAgreement": {
+    "fullDescription": {
+      "text": "Using insecure key agreement. Ensure the use of recommended key agreement by BSI TR-02102-1, which includes DH, ECDH/ECCDH and ECKA-EG with recommended hash functions."
+    },
+    "shortDescription": {
+      "text": "Use of insecure key agreement."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use key agreement based on DH, ECDH/ECCDH and ECKA-EG with recommended hash functions."
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/mark/bc-jca/mapping.yaml b/mark/bc-jca/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f72cabcca99fd7f2fb26c8f5f54ff567dcf23eac
--- /dev/null
+++ b/mark/bc-jca/mapping.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: Apache-2.0
+
+# Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#     _____          _
+#    / ____|        | |
+#   | |     ___   __| |_   _ _______
+#   | |    / _ \ / _` | | | |_  / _ \
+#   | |___| (_) | (_| | |_| |/ /  __/
+#    \_____\___/ \__,_|\__, /___\___|
+#                      __/ |
+#                     |___/
+#
+# This file is part of the MEDINA Framework.
+metrics:
+  - name: "SecureCryptographicPrimitives"
+    rules:
+      - "Ciphers"
+      - "HashFunctions"
+      - "MessageAuthenticationCodes"
+      - "Signatures"
+      - "SecureRandom"
+      - "KeyAgreement"
+    configuration:
+        default: false
+        operator: "=="
+        type: BOOLEAN
+        target: 
+          - true
diff --git a/mark/bc-jca/rules.mark b/mark/bc-jca/rules.mark
new file mode 100644
index 0000000000000000000000000000000000000000..50cac13828c07c9bbae3ffa9861cfc7d484f348a
--- /dev/null
+++ b/mark/bc-jca/rules.mark
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2020-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ *
+ * This file is part of the MEDINA framework.
+ */
+package de.fraunhofer.aisec.codyze.mark.bcjca
+
+/*
+ * Included providers
+ * - SUN version 17
+ * - SunRsaSign version 17
+ * - SunEC version 17
+ * - SunJCE version 17
+ * - BC version 1.76
+ */
+
+/**
+ * Check used ciphers transforms.
+ *
+ * Note: Recommendations are based on BSI TR-02102-1, version 2023-01
+ *  - Symmetric crypto
+ *    - ciphers: AES-128, AES-192, AES-256
+ *    - cipher modes: CCM, GCM, CTR, CBC
+ *    - padding (only for cipher mode CBC): ISO7816-4Padding, PKCS5Padding, PKCS7Padding
+ *  - Asymmetric crypto
+ *    - system/scheme: ECIES, DLIES, RSA
+ */
+rule Ciphers {
+    using
+        Cipher as c
+    ensure
+        c.transform in [
+            /* SunJCE version 17 */
+                /* AES with cipher modes CCM, GCM, CTR, CBC */
+                "AES/CTR/NOPADDING", 
+                "AES/CBC/PKCS5PADDING", 
+                "AES/GCM/NoPadding", 
+                "Cipher.AES_128/GCM/NoPadding", 
+                    "OID.2.16.840.1.101.3.4.1.6", "2.16.840.1.101.3.4.1.6", 
+                "AES_192/GCM/NoPadding", 
+                    "OID.2.16.840.1.101.3.4.1.26", "2.16.840.1.101.3.4.1.26", 
+                "AES_256/GCM/NoPadding", 
+                    "OID.2.16.840.1.101.3.4.1.46", "2.16.840.1.101.3.4.1.46", 
+                /* AES Password based Encryption */
+                "PBEWithHmacSHA256AndAES_128", 
+                "PBEWithHmacSHA384AndAES_128", 
+                "PBEWithHmacSHA512AndAES_128", 
+                "PBEWithHmacSHA256AndAES_256", 
+                "PBEWithHmacSHA384AndAES_256", 
+                "PBEWithHmacSHA512AndAES_256", 
+                /* RSA */
+                "RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING", 
+                "RSA/ECB/OAEPWITHSHA-512/256ANDMGF1PADDING", 
+                "RSA/ECB/OAEPWITHSHA-384ANDMGF1PADDING", 
+                "RSA/ECB/OAEPWITHSHA-512ANDMGF1PADDING", 
+            /* BC version 1.76 */
+                /* AES with cipher modes CCM, GCM, CTR, CBC */
+                "CCM", 
+                    "2.16.840.1.101.3.4.1.7", "OID.2.16.840.1.101.3.4.1.7", "2.16.840.1.101.3.4.1.27", "OID.2.16.840.1.101.3.4.1.27", "2.16.840.1.101.3.4.1.47", "OID.2.16.840.1.101.3.4.1.47", "1.2.410.200046.1.1.37", "OID.1.2.410.200046.1.1.37", "1.2.410.200046.1.1.38", "OID.1.2.410.200046.1.1.38", "1.2.410.200046.1.1.39", "OID.1.2.410.200046.1.1.39", 
+                "GCM", 
+                    "2.16.840.1.101.3.4.1.6", "OID.2.16.840.1.101.3.4.1.6", "2.16.840.1.101.3.4.1.26", "OID.2.16.840.1.101.3.4.1.26", "2.16.840.1.101.3.4.1.46", "OID.2.16.840.1.101.3.4.1.46", 
+                /* AES Password based Encryption */
+                "PBEWITHSHA256AND128BITAES-CBC-BC", 
+                    "1.3.6.1.4.1.22554.1.2.1.2.1.2", "OID.1.3.6.1.4.1.22554.1.2.1.2.1.2", "PBEWITHSHA-256AND128BITAES-CBC-BC", "PBEWITHSHA256AND128BITAES-BC", "PBEWITHSHA-256AND128BITAES-BC",
+                "PBEWITHSHA256AND192BITAES-CBC-BC",
+                    "1.3.6.1.4.1.22554.1.2.1.2.1.22", "OID.1.3.6.1.4.1.22554.1.2.1.2.1.22", "PBEWITHSHA-256AND192BITAES-CBC-BC", "PBEWITHSHA256AND192BITAES-BC", "PBEWITHSHA-256AND192BITAES-BC",
+                "PBEWITHSHA256AND256BITAES-CBC-BC", 
+                    "1.3.6.1.4.1.22554.1.2.1.2.1.42", "OID.1.3.6.1.4.1.22554.1.2.1.2.1.42", "PBEWITHSHA-256AND256BITAES-CBC-BC", "PBEWITHSHA256AND256BITAES-BC", "PBEWITHSHA-256AND256BITAES-BC",
+                /* ECIES */
+                "ECIESwithSHA256andAES-CBC", 
+                "ECIESwithSHA384andAES-CBC", 
+                "ECIESwithSHA512andAES-CBC"
+                /* DLIES */
+                /* none */
+            ]
+    fail
+}
+
+/**
+ * Check used hash functions (aka message digest).
+ *
+ * Note: Recommendations are based on BSI TR-02102-1, version 2023-01
+ *  - Hash functions: SHA-256, SHA-512/256, SHA-384, SHA-512, SHA3-256, SHA3-384, SHA3-512
+ */
+rule HashFunctions {
+    using
+        MessageDigest as md 
+    ensure
+        md.algorithm in [
+            /* SUN version 17 */
+                "SHA-256", 
+                    "OID.2.16.840.1.101.3.4.2.1", "2.16.840.1.101.3.4.2.1", "SHA256", 
+                "SHA-512/256", 
+                    "OID.2.16.840.1.101.3.4.2.6", "2.16.840.1.101.3.4.2.6", "SHA512/256", 
+                "SHA-384", 
+                    "OID.2.16.840.1.101.3.4.2.2", "2.16.840.1.101.3.4.2.2", "SHA384", 
+                "SHA-512", 
+                    "OID.2.16.840.1.101.3.4.2.3", "2.16.840.1.101.3.4.2.3", "SHA512", 
+                "SHA3-256", 
+                    "OID.2.16.840.1.101.3.4.2.8", "2.16.840.1.101.3.4.2.8", 
+                "SHA3-384", 
+                    "OID.2.16.840.1.101.3.4.2.9", "2.16.840.1.101.3.4.2.9", 
+                "SHA3-512", 
+                    "OID.2.16.840.1.101.3.4.2.10", "2.16.840.1.101.3.4.2.10", 
+            /* BC version 1.76 */
+                "SHA-256", 
+                    "SHA256", "2.16.840.1.101.3.4.2.1", 
+                "SHA-512/256", 
+                    "SHA512/256", "SHA512256", "SHA-512(256)", "SHA512(256)", "2.16.840.1.101.3.4.2.6", 
+                "SHA-384", 
+                    "SHA384", "2.16.840.1.101.3.4.2.2", 
+                "SHA-512", 
+                    "SHA512", "2.16.840.1.101.3.4.2.3", 
+                "SHA3-256", 
+                    "2.16.840.1.101.3.4.2.8", "OID.2.16.840.1.101.3.4.2.8",
+                "SHA3-384", 
+                    "2.16.840.1.101.3.4.2.9", "OID.2.16.840.1.101.3.4.2.9", 
+                "SHA3-512", 
+                    "2.16.840.1.101.3.4.2.10", "OID.2.16.840.1.101.3.4.2.10"
+            ]
+    fail 
+}
+
+/**
+ * Check used message authentication codes.
+ *
+ * Note: Recommendations are based on BSI TR-02102-1, version 2023-01
+ *  - CMAC -> using recommended block ciphers
+ *  - HMAC -> using recommended hash functions
+ *  - GMAC -> using recommended block ciphers
+ */
+rule MessageAuthenticationCodes {
+    using 
+        Mac as m 
+    ensure
+        m.algorithm in [
+            /* SunJCE version 17*/
+                /* HMAC */
+                "HmacPBESHA256", "HmacPBESHA512/256", "HmacPBESHA384", "HmacPBESHA512", 
+                "PBEWithHmacSHA256", "PBEWithHmacSHA384", "PBEWithHmacSHA512", 
+                "HmacSHA256", 
+                    "OID.1.2.840.113549.2.9", "1.2.840.113549.2.9", 
+                "HmacSHA512/256", 
+                    "OID.1.2.840.113549.2.13", "1.2.840.113549.2.13", 
+                "HmacSHA384", 
+                    "OID.1.2.840.113549.2.10", "1.2.840.113549.2.10", 
+                "HmacSHA512", 
+                    "OID.1.2.840.113549.2.11", "1.2.840.113549.2.11", 
+                "HmacSHA3-256", 
+                    "OID.2.16.840.1.101.3.4.2.14", "2.16.840.1.101.3.4.2.14", 
+                "HmacSHA3-384", 
+                    "OID.2.16.840.1.101.3.4.2.15", "2.16.840.1.101.3.4.2.15", 
+                "HmacSHA3-512", 
+                    "OID.2.16.840.1.101.3.4.2.16", "2.16.840.1.101.3.4.2.16", 
+            /* BC version 1.76 */
+                /* CMAC */
+                "AESCMAC", 
+                /* HMAC */
+                "PBEWITHHMACSHA256", "PBEWITHHMACSHA384", "PBEWITHHMACSHA512", 
+                "HMACSHA256", 
+                    "HMAC-SHA256", "HMAC/SHA256", "1.2.840.113549.2.9", "2.16.840.1.101.3.4.2.1", 
+                "HMACSHA512/256", "
+                    HMAC-SHA512/256", "HMAC/SHA512/256", 
+                "HMACSHA384", 
+                    "HMAC-SHA384", "HMAC/SHA384", "1.2.840.113549.2.10", 
+                "HMACSHA512", 
+                    "HMAC-SHA512", "HMAC/SHA512", "1.2.840.113549.2.11", 
+                "HMACSHA3-256", 
+                    "HMAC-SHA3-256", "HMAC/SHA3-256", "2.16.840.1.101.3.4.2.14", 
+                "HMACSHA3-384", 
+                    "HMAC-SHA3-384", "HMAC/SHA3-384", "2.16.840.1.101.3.4.2.15", 
+                "HMACSHA3-512", 
+                    "HMAC-SHA3-512", "HMAC/SHA3-512", "2.16.840.1.101.3.4.2.16", 
+                /* GMAC */
+                "AES-GMAC", 
+                    "AESGMAC"
+            ]
+    fail
+}
+
+rule Signatures {
+    using 
+        Signature as s 
+    ensure 
+        s.algorithm in [
+            /* SUN version 17 */
+                /* DSA */
+                "SHA256withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.2", "2.16.840.1.101.3.4.3.2", 
+                "SHA384withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.3", "2.16.840.1.101.3.4.3.3", 
+                "SHA512withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.4", "2.16.840.1.101.3.4.3.4", 
+                "SHA3-256withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.6", "2.16.840.1.101.3.4.3.6", 
+                "SHA3-384withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.7", "2.16.840.1.101.3.4.3.7", 
+                "SHA3-512withDSA", 
+                    "OID.2.16.840.1.101.3.4.3.8", "2.16.840.1.101.3.4.3.8", 
+                "SHA256withDSAinP1363Format", 
+                "SHA384withDSAinP1363Format", 
+                "SHA512withDSAinP1363Format", 
+                "SHA3-256withDSAinP1363Format", 
+                "SHA3-384withDSAinP1363Format", 
+                "SHA3-512withDSAinP1363Format", 
+            /* SunRsaSign version 17 */
+                /* RSA */
+                /* none */
+            /* SunEC version 17 */
+                /* ECDSA */
+                "SHA256withECDSA", 
+                    "OID.1.2.840.10045.4.3.2", "1.2.840.10045.4.3.2", 
+                "SHA384withECDSA", 
+                    "OID.1.2.840.10045.4.3.3", "1.2.840.10045.4.3.3", 
+                "SHA512withECDSA", 
+                    "OID.1.2.840.10045.4.3.4", "1.2.840.10045.4.3.4", 
+                "SHA3-256withECDSA", 
+                    "OID.2.16.840.1.101.3.4.3.10", "2.16.840.1.101.3.4.3.10", 
+                "SHA3-384withECDSA", 
+                    "OID.2.16.840.1.101.3.4.3.11", "2.16.840.1.101.3.4.3.11", 
+                "SHA3-512withECDSA", 
+                    "OID.2.16.840.1.101.3.4.3.12", "2.16.840.1.101.3.4.3.12", 
+                "SHA256withECDSAinP1363Format", 
+                "SHA384withECDSAinP1363Format", 
+                "SHA512withECDSAinP1363Format", 
+                "SHA3-256withECDSAinP1363Format", 
+                "SHA3-384withECDSAinP1363Format", 
+                "SHA3-512withECDSAinP1363Format", 
+            /* BC version 1.76*/
+                /* RSA - EMSA-PSS */
+                "SHA256WITHRSAANDMGF1", 
+                    "SHA256withRSA/PSS", "SHA256WithRSA/PSS", "SHA256WITHRSA/PSS", "SHA256withRSASSA-PSS", "SHA256WithRSASSA-PSS", "SHA256WITHRSASSA-PSS", "SHA256withRSAandMGF1", "SHA256WithRSAAndMGF1", 
+                "SHA512(256)WITHRSAANDMGF1", 
+                    "SHA512(256)withRSA/PSS", "SHA512(256)WithRSA/PSS", "SHA512(256)WITHRSA/PSS", "SHA512(256)withRSASSA-PSS", "SHA512(256)WithRSASSA-PSS", "SHA512(256)WITHRSASSA-PSS", "SHA512(256)withRSAandMGF1", "SHA512(256)WithRSAAndMGF1", 
+                "SHA384WITHRSAANDMGF1", 
+                    "SHA384withRSA/PSS", "SHA384WithRSA/PSS", "SHA384WITHRSA/PSS", "SHA384withRSASSA-PSS", "SHA384WithRSASSA-PSS", "SHA384WITHRSASSA-PSS", "SHA384withRSAandMGF1", "SHA384WithRSAAndMGF1", 
+                "SHA512WITHRSAANDMGF1", 
+                    "SHA512withRSA/PSS", "SHA512WithRSA/PSS", "SHA512WITHRSA/PSS", "SHA512withRSASSA-PSS", "SHA512WithRSASSA-PSS", "SHA512WITHRSASSA-PSS", "SHA512withRSAandMGF1", "SHA512WithRSAAndMGF1", 
+                "SHA3-256WITHRSAANDMGF1", 
+                    "SHA3-256withRSA/PSS", "SHA3-256WithRSA/PSS", "SHA3-256WITHRSA/PSS", "SHA3-256withRSASSA-PSS", "SHA3-256WithRSASSA-PSS", "SHA3-256WITHRSASSA-PSS", "SHA3-256withRSAandMGF1", "SHA3-256WithRSAAndMGF1", 
+                "SHA3-384WITHRSAANDMGF1", 
+                    "SHA3-384withRSA/PSS", "SHA3-384WithRSA/PSS", "SHA3-384WITHRSA/PSS", "SHA3-384withRSASSA-PSS", "SHA3-384WithRSASSA-PSS", "SHA3-384WITHRSASSA-PSS", "SHA3-384withRSAandMGF1", "SHA3-384WithRSAAndMGF1", 
+                "SHA3-512WITHRSAANDMGF1", 
+                    "SHA3-512withRSA/PSS", "SHA3-512WithRSA/PSS", "SHA3-512WITHRSA/PSS", "SHA3-512withRSASSA-PSS", "SHA3-512WithRSASSA-PSS", "SHA3-512WITHRSASSA-PSS", "SHA3-512withRSAandMGF1", "SHA3-512WithRSAAndMGF1", 
+                /* RSA - Digital Signature Scheme (DS) 2 und 3 */
+                "SHA256WITHRSA/ISO9796-2", 
+                    "SHA256withRSA/ISO9796-2", "SHA256WithRSA/ISO9796-2", 
+                "SHA512(256)WITHRSA/ISO9796-2",
+                    "SHA512(256)withRSA/ISO9796-2", "SHA512(256)WithRSA/ISO9796-2",  
+                "SHA384WITHRSA/ISO9796-2", 
+                    "SHA384withRSA/ISO9796-2", "SHA384WithRSA/ISO9796-2", 
+                "SHA512WITHRSA/ISO9796-2", 
+                    "SHA512withRSA/ISO9796-2", "SHA512WithRSA/ISO9796-2", 
+                /* DSA */
+                "SHA256WITHDSA", 
+                    "SHA256withDSA", "SHA256WithDSA", "SHA256/DSA", "2.16.840.1.101.3.4.3.2", "OID.2.16.840.1.101.3.4.3.2", 
+                "SHA384WITHDSA", 
+                    "SHA384withDSA", "SHA384WithDSA", "SHA384/DSA", "2.16.840.1.101.3.4.3.3", "OID.2.16.840.1.101.3.4.3.3", 
+                "SHA512WITHDSA", 
+                    "SHA512withDSA", "SHA512WithDSA", "SHA512/DSA", "2.16.840.1.101.3.4.3.4", "OID.2.16.840.1.101.3.4.3.4", 
+                "SHA3-256WITHDSA", 
+                    "SHA3-256withDSA", "SHA3-256WithDSA", "SHA3-256/DSA", "2.16.840.1.101.3.4.3.6", "OID.2.16.840.1.101.3.4.3.6", 
+                "SHA3-384WITHDSA", 
+                    "SHA3-384withDSA", "SHA3-384WithDSA", "SHA3-384/DSA", "2.16.840.1.101.3.4.3.7", "OID.2.16.840.1.101.3.4.3.7", 
+                "SHA3-512WITHDSA", 
+                    "SHA3-512withDSA", "SHA3-512WithDSA", "SHA3-512/DSA", "2.16.840.1.101.3.4.3.8", "OID.2.16.840.1.101.3.4.3.8", 
+                /* DSA - deterministic */
+                "SHA256WITHDDSA", 
+                "SHA384WITHDDSA", 
+                "SHA512WITHDDSA", 
+                "SHA3-256WITHDDSA", 
+                "SHA3-384WITHDDSA", 
+                "SHA3-512WITHDDSA", 
+                "SHA256WITHDETDSA", 
+                "SHA384WITHDETDSA", 
+                "SHA512WITHDETDSA", 
+                /* ECDSA */
+                "SHA256WITHECDSA", 
+                    "SHA256withECDSA", "SHA256WithECDSA", "SHA256/ECDSA", "1.2.840.10045.4.3.2", "OID.1.2.840.10045.4.3.2", 
+                "SHA384WITHECDSA", 
+                    "SHA384withECDSA", "SHA384WithECDSA", "SHA384/ECDSA", "1.2.840.10045.4.3.3", "OID.1.2.840.10045.4.3.3", 
+                "SHA512WITHECDSA", 
+                    "SHA512withECDSA", "SHA512WithECDSA", "SHA512/ECDSA", "1.2.840.10045.4.3.4", "OID.1.2.840.10045.4.3.4", 
+                "SHA3-256WITHECDSA", 
+                    "SHA3-256withECDSA", "SHA3-256WithECDSA", "SHA3-256/ECDSA", "2.16.840.1.101.3.4.3.10", "OID.2.16.840.1.101.3.4.3.10", 
+                "SHA3-384WITHECDSA", 
+                    "SHA3-384withECDSA", "SHA3-384WithECDSA", "SHA3-384/ECDSA", "2.16.840.1.101.3.4.3.11", "OID.2.16.840.1.101.3.4.3.11", 
+                "SHA3-512WITHECDSA", 
+                    "SHA3-512withECDSA", "SHA3-512WithECDSA", "SHA3-512/ECDSA", "2.16.840.1.101.3.4.3.12", "OID.2.16.840.1.101.3.4.3.12", 
+                "SHA256WITHCVC-ECDSA", 
+                    "SHA256withCVC-ECDSA", "SHA256WithCVC-ECDSA", "SHA256/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.3", "OID.0.4.0.127.0.7.2.2.2.2.3", 
+                "SHA384WITHCVC-ECDSA", 
+                    "SHA384withCVC-ECDSA", "SHA384WithCVC-ECDSA", "SHA384/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.4", "OID.0.4.0.127.0.7.2.2.2.2.4", 
+                "SHA512WITHCVC-ECDSA", 
+                    "SHA512withCVC-ECDSA", "SHA512WithCVC-ECDSA", "SHA512/CVC-ECDSA", "0.4.0.127.0.7.2.2.2.2.5", "OID.0.4.0.127.0.7.2.2.2.2.5", 
+                "SHA256WITHPLAIN-ECDSA", 
+                    "SHA256withPLAIN-ECDSA", "SHA256WithPLAIN-ECDSA", "SHA256/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.3", "OID.0.4.0.127.0.7.1.1.4.1.3", 
+                "SHA384WITHPLAIN-ECDSA", 
+                    "SHA384withPLAIN-ECDSA", "SHA384WithPLAIN-ECDSA", "SHA384/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.4", "OID.0.4.0.127.0.7.1.1.4.1.4", 
+                "SHA512WITHPLAIN-ECDSA", 
+                    "SHA512withPLAIN-ECDSA", "SHA512WithPLAIN-ECDSA", "SHA512/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.5", "OID.0.4.0.127.0.7.1.1.4.1.5", 
+                "SHA3-256WITHPLAIN-ECDSA", 
+                    "SHA3-256withPLAIN-ECDSA", "SHA3-256WithPLAIN-ECDSA", "SHA3-256/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.9", "OID.0.4.0.127.0.7.1.1.4.1.9", 
+                "SHA3-384WITHPLAIN-ECDSA", 
+                    "SHA3-384withPLAIN-ECDSA", "SHA3-384WithPLAIN-ECDSA", "SHA3-384/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.10", "OID.0.4.0.127.0.7.1.1.4.1.10", 
+                "SHA3-512WITHPLAIN-ECDSA", 
+                    "SHA3-512withPLAIN-ECDSA", "SHA3-512WithPLAIN-ECDSA", "SHA3-512/PLAIN-ECDSA", "0.4.0.127.0.7.1.1.4.1.11", "OID.0.4.0.127.0.7.1.1.4.1.11", 
+                /* ECDSA - deterministic */
+                "SHA256WITHECDDSA", 
+                    "SHA256WITHDETECDSA", 
+                "SHA384WITHECDDSA", 
+                    "SHA384WITHDETECDSA", 
+                "SHA512WITHECDDSA", 
+                    "SHA512WITHDETECDSA", 
+                "SHA3-256WITHECDDSA", 
+                "SHA3-384WITHECDDSA", 
+                "SHA3-512WITHECDDSA"
+            ]
+    fail
+}
+
+/**
+ * Check used message authentication codes.
+ *
+ * Note: Recommendations are based on BSI TR-02102-1, version 2023-01
+ */
+rule SecureRandom {
+    using 
+        SecureRandom as sr 
+    ensure 
+        sr.algorithm in [
+            /* SUN version 17 */
+                "NativePRNGBlocking", 
+                "DRBG", 
+            /* BC version 1.76 */
+                "DEFAULT", 
+                "NONCEANDIV"
+            ]
+    fail
+}
+
+/**
+ * Check used key agreement protocols.
+ *
+ * Note: Recommendations are based on BSI TR-02102-1, version 2023-01
+ */
+rule KeyAgreement {
+    using 
+        KeyAgreement as ka 
+    ensure 
+        ka.algorithm in [
+            /* BC version 1.76 */
+                /* DH */
+                "DHWITHSHA256KDF", 
+                "DHWITHSHA384KDF", 
+                "DHWITHSHA512KDF", 
+                /* ECDH / ECCDH */
+                "ECDHWITHSHA256KDF", 
+                    "1.3.132.1.11.1", "OID.1.3.132.1.11.1",
+                "ECDHWITHSHA384KDF", 
+                    "1.3.132.1.11.2", "OID.1.3.132.1.11.2",
+                "ECDHWITHSHA512KDF", 
+                    "1.3.132.1.11.3", "OID.1.3.132.1.11.3",
+                "ECCDHWITHSHA256KDF", 
+                    "1.3.132.1.14.1", "OID.1.3.132.1.14.1",
+                "ECCDHWITHSHA384KDF", 
+                    "1.3.132.1.14.2", "OID.1.3.132.1.14.2",
+                "ECCDHWITHSHA512KDF", 
+                    "1.3.132.1.14.3", "OID.1.3.132.1.14.3",
+                /* ECKA-EG */
+                "ECKAEGWITHSHA256KDF", 
+                    "0.4.0.127.0.7.1.1.5.1.1.3", "OID.0.4.0.127.0.7.1.1.5.1.1.3",
+                "ECKAEGWITHSHA384KDF", 
+                    "0.4.0.127.0.7.1.1.5.1.1.4", "OID.0.4.0.127.0.7.1.1.5.1.1.4",
+                "ECKAEGWITHSHA512KDF", 
+                    "0.4.0.127.0.7.1.1.5.1.1.5", "OID.0.4.0.127.0.7.1.1.5.1.1.5"
+            ]
+    fail
+}
diff --git a/mark/bc-jsse/LICENSE b/mark/bc-jsse/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/mark/bc-jsse/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/mark/bc-jsse/README.md b/mark/bc-jsse/README.md
index df628309017079fb171d8de0f9c2863ed511b02e..059f36f3da886ef4a68a5682a379ee9d088ff105 100644
--- a/mark/bc-jsse/README.md
+++ b/mark/bc-jsse/README.md
@@ -1,3 +1,5 @@
 # MARK for Bouncy Castle's JSSE Provider
 
-Example MARK specification files for a TLS server implemented using JSSE with Bouncy Castle as provide. The rule checks for an appropriate version of TLS.
+## License
+These files are part of the [MEDINA project](https://medina-project.eu/). 
+They are distributed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt). 
\ No newline at end of file
diff --git a/mark/bc-jsse/SSLContext.mark b/mark/bc-jsse/SSLContext.mark
index d078c67a8af2e4d522a052f682f7c3634d3edba0..528825c72eec62d04e4f20cb38b71d01c95a7fdf 100644
--- a/mark/bc-jsse/SSLContext.mark
+++ b/mark/bc-jsse/SSLContext.mark
@@ -1,3 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ *
+ * This file is part of the MEDINA Framework.
+ */
 package de.fraunhofer.aisec.codyze.bcjsse
 
 entity SSLContext {
diff --git a/mark/bc-jsse/SSLServerSocket.mark b/mark/bc-jsse/SSLServerSocket.mark
index 6be07b7fab715e22196eda84e68470003bea4f82..a8a38837f7fd437bddfc26f84355517797859af6 100644
--- a/mark/bc-jsse/SSLServerSocket.mark
+++ b/mark/bc-jsse/SSLServerSocket.mark
@@ -1,3 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ *
+ * This file is part of the MEDINA Framework.
+ */
 package de.fraunhofer.aisec.codyze.bcjsse
 
 entity SSLServerSocket {
diff --git a/mark/bc-jsse/SSLServerSocketFactory.mark b/mark/bc-jsse/SSLServerSocketFactory.mark
index 7521debd77e5f1defe6986b7df11855ccaaa356d..91edaa6bb4f1e94dfd0945c7a3915923838374df 100644
--- a/mark/bc-jsse/SSLServerSocketFactory.mark
+++ b/mark/bc-jsse/SSLServerSocketFactory.mark
@@ -1,3 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ *
+ * This file is part of the MEDINA Framework.
+ */
 package de.fraunhofer.aisec.codyze.bcjsse
 
 entity SSLServerSocketFactory {
diff --git a/mark/bc-jsse/findingDescription.json b/mark/bc-jsse/findingDescription.json
new file mode 100644
index 0000000000000000000000000000000000000000..7e9790b9aa1dfb8a9198e591994b995e3b7cb3f9
--- /dev/null
+++ b/mark/bc-jsse/findingDescription.json
@@ -0,0 +1,32 @@
+{
+  "TlsVersion": {
+    "fullDescription": {
+      "text": "TLS version isn't sufficiently restricted and may allow the use of deprecated version. Ensure the use of TLS versions 1.2 or 1.3."
+    },
+    "shortDescription": {
+      "text": "Insufficient restriction of TLS to version 1.2 or 1.3."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use TLS version 1.2 or 1.3."
+        }
+      }
+    ]
+  },
+  "TlsCipherSuites": {
+    "fullDescription": {
+      "text": "TLS cipher suites aren't sufficiently restricted and may allow the use of less secure or deprecated cipher suites. Ensure the use of: \"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_ECDSA_WITH_AES_128_CCM\", \"TLS_ECDHE_ECDSA_WITH_AES_256_CCM\", \"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384\", \"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\", \"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_DHE_RSA_WITH_AES_128_CCM\", \"TLS_DHE_RSA_WITH_AES_256_CCM\", \"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_DH_DSS_WITH_AES_128_CBC_SHA256\", \"TLS_DH_DSS_WITH_AES_256_CBC_SHA256\", \"TLS_DH_DSS_WITH_AES_128_GCM_SHA256\", \"TLS_DH_DSS_WITH_AES_256_GCM_SHA384\", \"TLS_DH_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DH_RSA_WITH_AES_256_CBC_SHA256\", \"TLS_DH_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_DH_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256\", \"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_PSK_WITH_AES_256_GCM_SHA38\", \"TLS_DHE_PSK_WITH_AES_128_CCM\", \"TLS_DHE_PSK_WITH_AES_256_CCM\", \"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256\", or \"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384\"."
+    },
+    "shortDescription": {
+      "text": "Insufficient restriction of TLS cipher suites."
+    },
+    "fixes": [
+      {
+        "description": {
+          "text": "Use any selection of the following TLS cipher suites: \"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_ECDSA_WITH_AES_128_CCM\", \"TLS_ECDHE_ECDSA_WITH_AES_256_CCM\", \"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256\", \"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384\", \"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\", \"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_DHE_RSA_WITH_AES_128_CCM\", \"TLS_DHE_RSA_WITH_AES_256_CCM\", \"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384\", \"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_DH_DSS_WITH_AES_128_CBC_SHA256\", \"TLS_DH_DSS_WITH_AES_256_CBC_SHA256\", \"TLS_DH_DSS_WITH_AES_128_GCM_SHA256\", \"TLS_DH_DSS_WITH_AES_256_GCM_SHA384\", \"TLS_DH_RSA_WITH_AES_128_CBC_SHA256\", \"TLS_DH_RSA_WITH_AES_256_CBC_SHA256\", \"TLS_DH_RSA_WITH_AES_128_GCM_SHA256\", \"TLS_DH_RSA_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256\", \"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384\", \"TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256\", \"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_DHE_PSK_WITH_AES_128_GCM_SHA256\", \"TLS_DHE_PSK_WITH_AES_256_GCM_SHA38\", \"TLS_DHE_PSK_WITH_AES_128_CCM\", \"TLS_DHE_PSK_WITH_AES_256_CCM\", \"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256\", \"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384\", \"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256\", or \"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384\"."
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/mark/bc-jsse/mapping.yaml b/mark/bc-jsse/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d160855e80fad14abc9b28e4ab2c05bd44ec9979
--- /dev/null
+++ b/mark/bc-jsse/mapping.yaml
@@ -0,0 +1,146 @@
+# SPDX-License-Identifier: Apache-2.0
+
+# Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#     _____          _
+#    / ____|        | |
+#   | |     ___   __| |_   _ _______
+#   | |    / _ \ / _` | | | |_  / _ \
+#   | |___| (_) | (_| | |_| |/ /  __/
+#    \_____\___/ \__,_|\__, /___\___|
+#                      __/ |
+#                     |___/
+#
+# This file is part of the MEDINA Framework.
+metrics:
+  - name: "TLSVersion"
+    rules:
+      - "TlsVersion"
+    configuration:
+        default: true
+        operator: ">"
+        type: STRING
+        target:
+          - "1.2"
+  - name: "TlsCipherSuites"
+    rules:
+      - "TlsCipherSuites"
+    configuration:
+        default: true
+        operator: "<="
+        type: STRING
+        target:
+          - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"
+          - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"
+          - "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"
+          - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"
+          - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"
+          - "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"
+          - "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"
+          - "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"
+          - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"
+          - "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_DHE_RSA_WITH_AES_128_CCM"
+          - "TLS_DHE_RSA_WITH_AES_256_CCM"
+          - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"
+          - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"
+          - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"
+          - "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"
+          - "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"
+          - "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"
+          - "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"
+          - "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"
+          - "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"
+          - "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"
+          - "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"
+          - "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"
+          - "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256"
+          - "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384"
+          - "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256"
+          - "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"
+          - "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"
+          - "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"
+          - "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"
+          - "TLS_DHE_PSK_WITH_AES_128_CCM"
+          - "TLS_DHE_PSK_WITH_AES_256_CCM"
+          - "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"
+          - "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"
+          - "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"
+          - "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"
+  - name: "TlsDHGroups"
+    rules:
+      - "TlsDHGroups"
+    configuration:
+        default: true
+        operator: "<="
+        type: STRING
+        target:
+        - "secp256r1"
+        - "secp384r1"
+        - "secp521r1"
+        - "brainpoolP256r1"
+        - "brainpoolP384r1"
+        - "brainpoolP512r1"
+        - "ffdhe2048"
+        - "ffdhe3072"
+        - "ffdhe4096"
+        - "brainpoolP256r1tls13"
+        - "brainpoolP384r1tls13"
+        - "brainpoolP512r1tls13"
+  - name: "TlsSignatureAlgorithms"
+    rules:
+      - "TlsSignatureAlgorithm"
+    configuration:
+        default: true
+        operator: "<="
+        type: STRING
+        target:
+          - "RSA+SHA256"
+          - "RSA+SHA384"
+          - "RSA+SHA512"
+          - "DSA+SHA256"
+          - "DSA+SHA384"
+          - "DSA+SHA512"
+          - "ECDSA+SHA256"
+          - "ECDSA+SHA384"
+          - "ECDSA+SHA512"
+          - "rsa_pss_rsae_sha256"
+          - "rsa_pss_rsae_sha384"
+          - "rsa_pss_rsae_sha512"
+          - "rsa_pss_pss_sha256"
+          - "rsa_pss_pss_sha384"
+          - "rsa_pss_pss_sha512"
+          - "ecdsa_secp256r1_sha256"
+          - "ecdsa_secp384r1_sha384"
+          - "ecdsa_brainpoolP256r1tls13_sha256"
+          - "ecdsa_brainpoolP384r1tls13_sha384"
+          - "ecdsa_brainpoolP512r1tls13_sha512"
+          - "rsa_pkcs1_sha256"
+          - "rsa_pkcs1_sha384"
+          - "rsa_pkcs1_sha512"
diff --git a/mark/bc-jsse/rules.mark b/mark/bc-jsse/rules.mark
index 0bf5ca5ddad5b749e05afa3608eaa02e0a27fdc6..0a293e693be51c5171ed0e63c61cf77ab3e31a0c 100644
--- a/mark/bc-jsse/rules.mark
+++ b/mark/bc-jsse/rules.mark
@@ -1,9 +1,96 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/*
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | |
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ *
+ * This file is part of the MEDINA Framework.
+ */
 package de.fraunhofer.aisec.codyze.mark.bcjsse
 
-rule ID_1 {
+rule TlsVersion {
     using 
         SSLServerSocket as socket
     ensure 
         _subset(socket.protocols, ["TLSv1.2", "TLSv1.3"])
     fail
 }
+
+rule TlsCipherSuites {
+    using
+        SSLServerSocket as socket
+    ensure
+        _subset(socket.ciphersuites,
+            ["TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+             "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+             "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+             "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+             "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
+             "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
+             "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+             "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+             "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+             "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+             "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
+             "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+             "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
+             "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
+             "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+             "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+             "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
+             "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
+             "TLS_DHE_RSA_WITH_AES_128_CCM",
+             "TLS_DHE_RSA_WITH_AES_256_CCM",
+             "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+             "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+             "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+             "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+             "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
+             "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
+             "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
+             "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
+             "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
+             "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
+             "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
+             "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
+             "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
+             "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
+             "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
+             "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
+             "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+             "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
+             "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
+             "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
+             "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
+             "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
+             "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
+             "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
+             "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
+             "TLS_DHE_PSK_WITH_AES_128_CCM",
+             "TLS_DHE_PSK_WITH_AES_256_CCM",
+             "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
+             "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
+             "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
+             "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"])
+    fail
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 2f5d7f5892f45796012ea0406641d1fc96c57db1..5ea33066feec66cbda39e8e2dfd4689a9017da4f 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,4 +1,4 @@
-rootProject.name = "codyze"
+rootProject.name = "codyze-medina"
 
 include("generator_orchestrator")
 project(":generator_orchestrator").projectDir = file("build/generator_orchestrator")
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeMedina.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeMedina.kt
deleted file mode 100644
index d67383b201aba094334105d6bebbe42a3501c28a..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeMedina.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina
-
-import de.fraunhofer.aisec.codyze.analysis.AnalysisServer
-import de.fraunhofer.aisec.codyze.analysis.Finding
-import de.fraunhofer.aisec.codyze.medina.util.*
-import java.util.concurrent.Callable
-import java.util.concurrent.TimeUnit
-import kotlin.streams.toList
-import kotlin.system.exitProcess
-import mu.KotlinLogging
-import org.openapitools.client.orchestrator.model.Metric
-import picocli.CommandLine
-import picocli.CommandLine.Command
-import picocli.CommandLine.Mixin
-
-@Command(name = "codyze", mixinStandardHelpOptions = true)
-class CodyzeMedina(config: Configuration, args: Array<String>) : Callable<Int> {
-    private val logger = KotlinLogging.logger {}
-    @Mixin val configuration: Configuration = config
-    private val cmdlineArgs = args
-
-    /**
-     * Callable function containing the core program logic
-     * @author Florian Wendland
-     * @author Robert Haimerl
-     * @return 1 when any of the Findings is problematic, 0 else
-     */
-    override fun call(): Int {
-        configuration.validate()
-
-        val connection =
-            Connection(
-                configuration.orchestrator.orchestratorEndpoint.toString(),
-                configuration.orchestrator.auth.oauthEndpoint.toString(),
-                configuration.orchestrator.auth.username,
-                configuration.orchestrator.auth.password
-            )
-
-        val config =
-            de.fraunhofer.aisec.codyze.config.Configuration.initConfig(
-                configuration.configFile.path.toFile(),
-                *cmdlineArgs,
-                "--default-passes",
-                "--passes+",
-                "IdentifierPass",
-                "--passes+",
-                "EdgeCachePass",
-            )
-
-        // do not allow LSP/TUI
-        if (config.executionMode.isLsp || config.executionMode.isTui) {
-            logger.warn { "Forbidden execution mode, changing to CLI" }
-            config.executionMode.isCli = true
-            config.executionMode.isLsp = false
-            config.executionMode.isTui = false
-        }
-
-        val server = AnalysisServer(config)
-        server.start()
-        val ctx = server.analyze(config.source)[config.timeout, TimeUnit.MINUTES]
-        val findings = ctx.findings
-
-        environment = configuration.ci
-        if (environment == Configuration.Environment.NONE) verifyEnvironment()
-        addEnvironmentVariables()
-        fetchGitHash()
-        printFindings(findings, config)
-
-        parseMapping("mappings.txt")
-
-        // convert the Set<Metric> to a List<String> consisting of the metric names
-        val metricNameList =
-            connection
-                .fetchMetrics()
-                .stream()
-                .map { m: Metric -> m.name }
-                .filter { n: String? -> n != null }
-                .map { n: String? -> n!! }
-                .toList()
-        val assessmentTool = createAssessmentTool(metricNameList)
-        val evidence = createEvidence(config.output)
-        val results =
-            findings
-                .stream()
-                .map { f -> findingToAR(f, evidence.id ?: "") }
-                .toList()
-                .filterNotNull()
-                .toTypedArray()
-
-        // FIXME not implemented in orchestrator yet
-        // connection.registerAssessmentTool(assessmentTool)
-        connection.storeEvidence(evidence)
-        connection.sendAssessmentResults(results)
-
-        // Return code based on the existence of violations
-        return if (findings.stream().anyMatch { obj: Finding -> obj.isProblem }) 1 else 0
-    }
-}
-
-private val logger = KotlinLogging.logger {}
-
-/**
- * Entry point of the program
- * @args cli arguments provided
- * @author Florian Wendland
- * @author Robert Haimerl
- */
-fun main(args: Array<String>) {
-    // initialize configuration
-    val config = Configuration.initialize(args)
-    // parse remaining CLI options and overwrite defaults and options from config file
-    val returnValue =
-        CommandLine(CodyzeMedina(config, args)).setUnmatchedArgumentsAllowed(true).execute(*args)
-    logger.info { "Exit with return value: $returnValue" }
-    exitProcess(returnValue)
-}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Connection.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Connection.kt
deleted file mode 100644
index ec560a233121847ea849d95ba8ac9cad3a7e11fe..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Connection.kt
+++ /dev/null
@@ -1,329 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina
-
-import de.fraunhofer.aisec.codyze.medina.auth.*
-import java.net.InetAddress
-import java.net.InetSocketAddress
-import java.net.Socket
-import java.net.URL
-import mu.KotlinLogging
-import org.dmfs.oauth2.client.OAuth2AccessToken
-import org.openapitools.client.evidence.api.EvidenceStoreApi
-import org.openapitools.client.evidence.model.Evidence
-import org.openapitools.client.orchestrator.api.OrchestratorApi
-import org.openapitools.client.orchestrator.model.AssessmentResult
-import org.openapitools.client.orchestrator.model.AssessmentTool
-import org.openapitools.client.orchestrator.model.Metric
-
-/**
- * the Connection Object handles the OAuth2 Token as well as the communication with the Orchestrator
- * @author Robert Haimerl
- * @param orchestratorURL the URL of the Orchestrator instance
- * @param tokenURL the URL of the OAuth2 instance
- * @param username the OAuth2 username
- * @param password the OAuth2 password
- */
-class Connection(
-    private val orchestratorURL: String,
-    private val tokenURL: String,
-    username: String,
-    password: String
-) {
-    private val logger = KotlinLogging.logger {}
-    private val authClient = getOAuth2Client(tokenURL, username, password)
-    private val orchestratorClient =
-        org.openapitools.client.orchestrator.ApiClient().setBasePath(orchestratorURL)
-    private val orchestratorApi = OrchestratorApi(orchestratorClient)
-    private val evidenceClient =
-        org.openapitools.client.evidence.ApiClient().setBasePath(orchestratorURL)
-    private val evidenceApi = EvidenceStoreApi(evidenceClient)
-    // Fetches a token instantly after creating the Connection object
-    private var token: OAuth2AccessToken? = getToken()
-
-    /**
-     * Test the connection to the OAuth2-Servers
-     * @author Robert Haimerl
-     * @return whether a connection could be established
-     */
-    fun testOAuthConnection(): Boolean {
-        logger.info { "Testing the connection to the OAuth-Server" }
-        // fist do a simple ping
-        val tokenHost = URL(tokenURL).host
-        if (!isReachable(InetAddress.getByName(tokenHost), 80, 2000)) {
-            logger.error { "Failed to ping OAuth-Server" }
-            return false
-        }
-        // then try to request a token with the given credentials
-        if (getAccessToken(authClient) == null) {
-            logger.error { "Failed to retrieve a token with the given credentials" }
-            return false
-        }
-        return true
-    }
-
-    /**
-     * Test the connection to the Orchestrator-Server
-     * @author Robert Haimerl
-     * @return whether a connection could be established
-     */
-    fun testOrchestratorConnection(): Boolean {
-        logger.info { "Testing the connection to the Orchestrator" }
-        // first ping the server
-        val orchestratorHost = URL(orchestratorURL).host
-        if (!isReachable(InetAddress.getByName(orchestratorHost), 80, 2000)) {
-            logger.error { "Failed to ping orchestrator" }
-            return false
-        }
-        // then try to make a simple get request
-        applyToken()
-        return isTokenValid()
-    }
-
-    /**
-     * retrieves access token with the client created by the given credentials
-     * @author Robert Haimerl
-     * @return the token or null if there was an error
-     */
-    private fun getToken(): OAuth2AccessToken? {
-        return getAccessToken(authClient)
-    }
-
-    /**
-     * applies the OAuth2-Token to both the Orchestrator Client and the Evidence Client
-     * @author Robert Haimerl
-     * @return whether there was a token that could be applied
-     */
-    private fun applyToken(): Boolean {
-        val tk = token ?: return false
-        orchestratorClient.addDefaultHeader(
-            "Authorization",
-            "${tk.tokenType()} ${tk.accessToken()}"
-        )
-        evidenceClient.addDefaultHeader("Authorization", "${tk.tokenType()} ${tk.accessToken()}")
-        logger.info { "Access token successfully applied to the connection" }
-        return true
-    }
-
-    /**
-     * Sends the AssessmentResults to the orchestrator
-     * @author Robert Haimerl
-     * @param ar the AssessmentResults to send
-     * @param retryOnError whether it should retry the process on a connection error
-     * @return whether sending was successful
-     */
-    fun sendAssessmentResults(ar: Array<AssessmentResult>?, retryOnError: Boolean = true): Boolean {
-        if (ar == null || ar.isEmpty()) {
-            logger.warn { "No AssessmentResults to send" }
-            return true
-        }
-        if (token == null) {
-            logger.error { "No auth token: aborting" }
-            return false
-        }
-        if (!isTokenValid()) {
-            logger.warn { "Token invalid, requesting a new one" }
-            token = getToken()
-            applyToken()
-        }
-        var nextElem = 0
-        try {
-            logger.info {
-                "trying to send AssessmentResults to ${orchestratorApi.apiClient.basePath}"
-            }
-            for (result in ar) {
-                orchestratorApi.orchestratorStoreAssessmentResult(result)
-                nextElem++
-                logger.debug { "Sent assessment result with id " + result.id }
-            }
-        } catch (ae: org.openapitools.client.orchestrator.ApiException) {
-            logger.error { "Could not send to ${orchestratorApi.apiClient.basePath}" }
-            return if (retryOnError && ae.code == 401) {
-                logger.info { "Retrying transmission process..." }
-                // retries the transmission process starting from the failed element
-                sendAssessmentResults(ar.copyOfRange(nextElem, ar.size), false)
-            } else false
-        }
-        logger.info { "All assessment results sent to ${orchestratorApi.apiClient.basePath}" }
-        return true
-    }
-
-    // FIXME: methods surrounding assessment tools seem to be unimplemented
-    /**
-     * Registers an AssessmentTool. This previously checks whether the tool was already registered
-     * @author Robert Haimerl
-     * @param tool the AssessmentTool to register
-     * @return whether the tool could be registered successfully
-     */
-    fun registerAssessmentTool(tool: AssessmentTool): Boolean {
-        if (!confirmToken()) return false
-        logger.info { "Trying to set the assessment tool to \"${tool.name}\" (${tool.id})" }
-        // first check whether the tool is already registered
-        return try {
-            val registeredTool = orchestratorApi.orchestratorGetAssessmentTool(tool.id)
-            if (registeredTool == null) {
-                // if no such tool is known (null returned): try to register
-                try {
-                    orchestratorApi.orchestratorRegisterAssessmentTool(tool)
-                    logger.info { "Successfully registered new tool" }
-                    true
-                } catch (e: org.openapitools.client.orchestrator.ApiException) {
-                    logger.error { "Failed to register assessment tool" }
-                    false
-                }
-            } else if (!registeredTool.equals(tool)) {
-                // if the id is known, but it is not identical: try to update
-                try {
-                    orchestratorApi.orchestratorUpdateAssessmentTool(tool.id, tool)
-                    logger.info { "Successfully updated the tool" }
-                    true
-                } catch (e: org.openapitools.client.orchestrator.ApiException) {
-                    logger.error { "Failed to update assessment tool" }
-                    false
-                }
-            } else {
-                // if the tool is known and identical: no action required
-                logger.info { "Tool was already registered" }
-                true
-            }
-        } catch (e: org.openapitools.client.orchestrator.ApiException) {
-            logger.error { "Failed to fetch assessment tool with id ${tool.id}" }
-            false
-        }
-    }
-
-    /**
-     * Stores the Evidence in the Orchestrator
-     * @author Robert Haimerl
-     * @param evidence the Evidence to store
-     * @return whether the Evidence could be successfully stored
-     */
-    fun storeEvidence(evidence: Evidence): Boolean {
-        if (!confirmToken()) return false
-        return try {
-            val res = evidenceApi.evidenceStoreStoreEvidence(evidence)
-            logger.info { "Successfully stored evidence with id ${evidence.id}" }
-            logger.info {
-                "Response has status '${res.status}' with status message '${res.statusMessage}'"
-            }
-            true
-        } catch (e: org.openapitools.client.evidence.ApiException) {
-            logger.error { "Failed to store evidence with id ${evidence.id}" }
-            false
-        }
-    }
-
-    /**
-     * Fetches all available metrics from the catalog
-     * @author Robert Haimerl
-     * @return a Set of the metrics
-     */
-    fun fetchMetrics(): Set<Metric> {
-        if (!confirmToken()) return setOf()
-        val metricSet = mutableSetOf<Metric>()
-        var nextPage: String? = null
-        try {
-            do {
-                val response = orchestratorApi.orchestratorListMetrics(null, nextPage, null, null)
-                if (response.metrics != null) metricSet.addAll(response.metrics!!)
-                nextPage = response.nextPageToken
-            } while (nextPage != null)
-        } catch (e: org.openapitools.client.orchestrator.ApiException) {
-            logger.error { "Failed to fetch the metrics from the catalog" }
-        } catch (e: ClassCastException) {
-            logger.error { "Response could not be parsed properly" }
-        }
-        return metricSet
-    }
-
-    /**
-     * Checks if the given address/port combination is reachable. Used since some hosts do not
-     * respond to regular isReachable on port 7
-     * @author Robert Haimerl
-     * @param addr the INetAddress we try to ping
-     * @param port the according port
-     * @param timeout when every attempt should time out (a maximum of two connection attempts are
-     * made)
-     * @return whether the address could be reached
-     */
-    @Suppress("SameParameterValue")
-    private fun isReachable(addr: InetAddress, port: Int = 0, timeout: Int = 2000): Boolean {
-        // first try default isReachable (which tries ICMP or port 7)
-        if (!addr.isReachable(timeout)) {
-            // otherwise, try manual connection to specified port
-            if (port < 0 || port > 65535) return false
-            val sockaddr = InetSocketAddress(addr, port)
-            val sock = Socket()
-            try {
-                sock.connect(sockaddr, timeout)
-            } catch (exc: Exception) {
-                return false
-            }
-        }
-        return true
-    }
-
-    /**
-     * Checks if the current OAuth-token is still valid (uses a simple GET call)
-     * @author Robert Haimerl
-     * @returns false on a APIException
-     */
-    private fun isTokenValid(): Boolean {
-        return try {
-            applyToken()
-            orchestratorApi.orchestratorListAssessmentResults(1, null, null, null)
-            true
-        } catch (e: org.openapitools.client.orchestrator.ApiException) {
-            e.printStackTrace()
-            false
-        }
-    }
-
-    /**
-     * Checks the OAuth-token and requests a new one if the current token is invalid
-     * @author Robert Haimerl
-     * @return false if we could not get a valid token
-     */
-    private fun confirmToken(): Boolean {
-        if (token == null) {
-            logger.error { "No auth token: aborting" }
-            return false
-        }
-        if (!isTokenValid()) {
-            logger.warn { "Token invalid, requesting a new one" }
-            token = getToken()
-            applyToken()
-            if (!isTokenValid()) {
-                logger.error { "Re-requested token is also invalid: aborting" }
-                return false
-            }
-        }
-        return true
-    }
-}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Assembler.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Assembler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2767a9671a3424c348ba9938be499e5599f6dd8c
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Assembler.kt
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.assembling
+
+import de.fraunhofer.aisec.codyze.analysis.Finding
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
+import de.fraunhofer.aisec.codyze.medina.main.CodyzeMedina
+import de.fraunhofer.aisec.codyze.medina.mapping.Mapping
+import de.fraunhofer.aisec.codyze.medina.mapping.RESOURCE_TYPE
+import de.fraunhofer.aisec.codyze.medina.mapping.Type
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.IOException
+import java.nio.file.Path
+import java.util.*
+import kotlin.io.path.readText
+import org.openapitools.client.evidence.model.Evidence
+import org.openapitools.client.orchestrator.model.AssessmentResult
+import org.openapitools.client.orchestrator.model.MetricConfiguration
+
+class Assembler(private val environment: Environment, private val serviceId: String) {
+    private val logger = KotlinLogging.logger {}
+    private val toolId = "codyze-medina-${CodyzeMedina.CODYZE_VERSION}"
+
+    /**
+     * Creates an Evidence based on a Codyze or MEDINA result
+     *
+     * @param resultFilePath the path to the results file used as evidence
+     * @return the resulting Evidence
+     * @author Robert Haimerl
+     */
+    fun createEvidence(resultFilePath: Path): Evidence {
+        logger.debug { "Creating new Evidence based on $resultFilePath" }
+        val ev = Evidence()
+
+        ev.id = UUID.randomUUID().toString()
+        ev.timestamp = java.time.OffsetDateTime.now()
+        ev.cloudServiceId = serviceId
+        ev.toolId = toolId
+        ev.raw =
+            try {
+                resultFilePath.readText()
+            } catch (e: IOException) {
+                logger.warn {
+                    "Evidence with id ${ev.id} has no raw content since the results file at $resultFilePath could not be read"
+                }
+                ""
+            }
+        ev.resource =
+            object {
+                @Suppress("unused") val id = environment.getGitHash()
+            }
+        return ev
+    }
+
+    /**
+     * Creates an AssessmentResult from the EvaluationResult creating from evaluating a MEDINA
+     * metric
+     *
+     * @param rule The rule covered by this result
+     * @param evaluationResult The result of the evaluation
+     * @param evidenceId the id of the Evidence belonging to the AssessmentResult
+     * @return the resulting AssessmentResult or null if not included in mapping.txt
+     * @author Robert Haimerl
+     */
+    fun convertEvaluationToAssessmentResult(
+        rule: Rule,
+        evaluationResult: EvaluationResult,
+        evidenceId: String,
+        hash: String
+    ): AssessmentResult {
+        logger.debug {
+            "Creating AssessmentResult with evidence $evidenceId from result $evaluationResult"
+        }
+
+        val metricConfiguration = MetricConfiguration()
+        metricConfiguration.operator = rule.operator
+        metricConfiguration.targetValue = rule.targetValue
+        metricConfiguration.isDefault = true
+        metricConfiguration.updatedAt = java.time.OffsetDateTime.now()
+        metricConfiguration.metricId = rule.name
+        metricConfiguration.cloudServiceId = serviceId
+
+        val assessmentResult = AssessmentResult()
+        assessmentResult.metricConfiguration = metricConfiguration
+        assessmentResult.id = UUID.randomUUID().toString()
+        assessmentResult.timestamp = java.time.OffsetDateTime.now()
+        assessmentResult.evidenceId = evidenceId
+        assessmentResult.resourceId = hash
+        assessmentResult.addResourceTypesItem(RESOURCE_TYPE)
+        assessmentResult.metricId = rule.name
+        assessmentResult.compliant = evaluationResult.isValidated()
+        assessmentResult.nonComplianceComments = evaluationResult.getDetailedMessage()
+        assessmentResult.cloudServiceId = serviceId
+        assessmentResult.toolId = toolId
+
+        return assessmentResult
+    }
+
+    /**
+     * Creates AssessmentResults from a Finding
+     *
+     * @param findings the Findings produced by Codyze
+     * @param evidenceId the id of the Evidence belonging to the AssessmentResult
+     * @return the resulting AssessmentResult or null if not included in mapping.txt
+     * @author Robert Haimerl
+     */
+    fun convertFindingsToAssessmentResults(
+        mapping: Mapping,
+        findings: Set<Finding>,
+        evidenceId: String,
+        hash: String
+    ): Array<AssessmentResult> {
+        logger.debug { "Creating AssessmentResults with evidence $evidenceId from set of Findings" }
+
+        val results = mutableListOf<AssessmentResult>()
+        val findingIdentifiers = findings.map { it.identifier }.toList()
+        for (metric in mapping.metrics) {
+            logger.debug { "Creating an AssessmentResult for the metric ${metric.name}" }
+            // considers metric only if all specified rules exist as Findings
+            if (metric.rules.all { findingIdentifiers.contains(it) }) {
+                val metricConfiguration = MetricConfiguration()
+                metricConfiguration.operator = metric.configuration.operator
+                metricConfiguration.isDefault = metric.configuration.default
+                // map the target values to their defined type - falls back to String if double
+                // cannot be parsed
+                val target =
+                    when (metric.configuration.type) {
+                        Type.NUMBER ->
+                            try {
+                                metric.configuration.target.map { it.toString().toDouble() }
+                            } catch (nfe: NumberFormatException) {
+                                logger.error {
+                                    "Error while trying to parse defined target of ${metric.name} (${metric.configuration.target}) as a number. Falling back to String target."
+                                }
+                                metric.configuration.target.map { it.toString() }
+                            }
+                        Type.BOOLEAN -> metric.configuration.target.map { it.toString() == "true" }
+                        else -> metric.configuration.target.map { it.toString() }
+                    }
+                // give the target value or a list of values if specified
+                metricConfiguration.targetValue = if (target.size == 1) target[0] else target
+                metricConfiguration.updatedAt = java.time.OffsetDateTime.now()
+                metricConfiguration.metricId = metric.name
+                metricConfiguration.cloudServiceId = serviceId
+
+                val assessmentResult = AssessmentResult()
+                assessmentResult.metricConfiguration = metricConfiguration
+                assessmentResult.id = UUID.randomUUID().toString()
+                assessmentResult.cloudServiceId = serviceId
+                assessmentResult.timestamp = java.time.OffsetDateTime.now()
+                assessmentResult.evidenceId = evidenceId
+                assessmentResult.resourceId = hash
+                assessmentResult.addResourceTypesItem(RESOURCE_TYPE)
+                assessmentResult.metricId = metric.name
+                // only see the metric as compliant if all of its rules are
+                val relevantFindings = findings.filter { metric.rules.contains(it.identifier) }
+                assessmentResult.compliant = relevantFindings.all { !it.isProblem }
+                // merges all log messages of findings that failed
+                assessmentResult.nonComplianceComments =
+                    relevantFindings
+                        .filter { it.isProblem }
+                        .map { "${it.identifier}: ${it.logMsg}\n" }
+                        .fold(StringBuilder()) { text, line -> text.append(line) }
+                        .toString()
+                assessmentResult.toolId = toolId
+
+                results.add(assessmentResult)
+            }
+        }
+        return results.toTypedArray()
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Environment.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Environment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3694b252aac33495007f89b27547e4f4a7bfa21f
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Environment.kt
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.assembling
+
+import de.fraunhofer.aisec.codyze.medina.main.Configuration.CIEnvironment
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStreamReader
+import java.nio.file.Path
+import kotlin.io.path.absolute
+
+class Environment(private var ciEnvironment: CIEnvironment, private val configPath: Path) {
+    // This lateinit variable is initialized at the first call of getGitHash()
+    private lateinit var gitHash: String
+    private val logger = KotlinLogging.logger {}
+    private var environmentVariables = mutableMapOf<String, String>()
+
+    init {
+        if (ciEnvironment == CIEnvironment.NONE) {
+            ciEnvironment = getEnvironment()
+        }
+        addEnvironmentVariables()
+    }
+
+    /**
+     * Tries multiple known environment variables to find out which CI/CD environment is being used
+     * Currently possible values for environment are ["None", "GitHub", "GitLab", "Jenkins"]. This
+     * method sets the "environment"-variable and fills the "environmentVariables"-Map specific to
+     * this environment
+     *
+     * @return the matched CIEnvironment
+     * @author Robert Haimerl
+     */
+    private fun getEnvironment(): CIEnvironment {
+        // all known CI/CD environments set "CI" to "true"
+        if (System.getenv("CI") != "true") {
+            logger.warn { "No CI/CD environment recognized" }
+            return CIEnvironment.NONE
+        }
+
+        val githubVars = arrayOf("GITHUB_ACTION", "GITHUB_JOB", "GITHUB_SHA")
+        val gitlabVars = arrayOf("CI_PIPELINE_ID", "CI_JOB_ID", "CI_COMMIT_SHA")
+        val jenkinsVars = arrayOf("JOB_NAME", "BUILD_ID", "JENKINS_URL")
+
+        if (githubVars.all { variable -> System.getenv(variable) != null }) {
+            logger.info { "Determined CI/CD environment to be GitHub" }
+            return CIEnvironment.GITHUB
+        } else if (gitlabVars.all { variable -> System.getenv(variable) != null }) {
+            logger.info { "Determined CI/CD environment to be GitLab" }
+            return CIEnvironment.GITLAB
+        } else if (jenkinsVars.all { variable -> System.getenv(variable) != null }) {
+            logger.info { "Determined CI/CD environment to be Jenkins" }
+            return CIEnvironment.JENKINS
+        }
+
+        logger.warn { "CI/CD environment could not be determined" }
+        return CIEnvironment.NONE
+    }
+
+    /**
+     * Adds all relevant environment variables for the detected CI environment to the Map
+     *
+     * @author Robert Haimerl
+     */
+    private fun addEnvironmentVariables() {
+        // jenkins: "SCM-specific variables such as GIT_COMMIT are not automatically defined as
+        // environment variables; rather you can use the return value of the checkout step"
+        when (ciEnvironment) {
+            CIEnvironment.GITHUB -> {
+                environmentVariables["commit_hash"] = "GITHUB_SHA"
+            }
+            CIEnvironment.GITLAB -> {
+                environmentVariables["commit_hash"] = "CI_COMMIT_SHA"
+            }
+            CIEnvironment.JENKINS -> {
+                environmentVariables["commit_hash"] = "GIT_COMMIT"
+            }
+            else -> return
+        }
+    }
+
+    /**
+     * This function tries to initialize the git hash exactly once, after that it will just refer to
+     * the result of the first attempt. This removes overhead by trying to figure out the commit
+     * hash every time anew.
+     *
+     * The function tries environment variables set by GitHub actions, then GitLab CI/CD before
+     * ultimately falling back to console commands.
+     *
+     * @return The git hash or "invalid"
+     * @author Robert Haimerl
+     */
+    fun getGitHash(): String {
+        if (!::gitHash.isInitialized) {
+            var hash: String?
+            // Default to "GITHUB_SHA" to prevent NullPointerExceptions from being thrown
+            hash = System.getenv(environmentVariables["commit_hash"] ?: "GITHUB_SHA")
+            if (hash == null) {
+                logger.warn { "Git commit hash could not be parsed from the CI Environment" }
+                hash =
+                    try {
+                        // Use the location of the configuration file in order to resolve the git
+                        // information
+                        val process =
+                            Runtime.getRuntime()
+                                .exec("git rev-parse HEAD", null, configPath.absolute().toFile())
+                        val reader = BufferedReader(InputStreamReader(process.inputStream))
+                        reader.readLine()
+                    } catch (e: IOException) {
+                        logger.error(e) {
+                            "IOException while trying to manually resolve the git commit hash: ${e.localizedMessage}"
+                        }
+                        null
+                    }
+            }
+            // If we couldn't figure hash out, log error and return "invalid"
+            gitHash =
+                if (hash == null) {
+                    logger.error { "Failed to determine an associated Git commit hash" }
+                    "invalid"
+                } else {
+                    hash
+                }
+        }
+        return gitHash
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/auth/Auth.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/auth/Auth.kt
deleted file mode 100644
index 76e762a21c55677857476a30862aae3952a7aa4f..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/auth/Auth.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina.auth
-
-import java.net.URI
-import mu.KotlinLogging
-import org.dmfs.httpessentials.client.HttpRequestExecutor
-import org.dmfs.httpessentials.httpurlconnection.HttpUrlConnectionExecutor
-import org.dmfs.oauth2.client.*
-import org.dmfs.oauth2.client.grants.ClientCredentialsGrant
-import org.dmfs.oauth2.client.grants.TokenRefreshGrant
-import org.dmfs.oauth2.client.scope.BasicScope
-import org.dmfs.rfc3986.uris.EmptyUri
-import org.dmfs.rfc5545.Duration
-
-private val logger = KotlinLogging.logger {}
-
-/**
- * Creates an OAuth2Client using the URL, username and password. The default Token ttl is set to 10
- * hours
- * @author Florian Wendland
- * @author Robert Haimerl
- * @param tokenURL the URL of the token-granting server
- * @param username the username used for authentication
- * @param password the password used for authentication
- * @return an OAuth2Client used to request tokens
- */
-fun getOAuth2Client(tokenURL: String, username: String, password: String): OAuth2Client {
-    // create OAuth2 provider
-    val pv: OAuth2AuthorizationProvider =
-        BasicOAuth2AuthorizationProvider(
-            null,
-            URI.create(tokenURL),
-            Duration(1, 0, 36000) // token expires after 10 hours
-        )
-    // Create OAuth2 client credentials
-    val cred: OAuth2ClientCredentials = BasicOAuth2ClientCredentials(username, password)
-    // Create OAuth2 client
-    return BasicOAuth2Client(pv, cred, EmptyUri())
-}
-
-/**
- * Requests a new access-token
- * @author Robert Haimerl
- * @param client an OAuth2Client containing the corresponding URL and credentials
- * @return an OAuth2AccessToken or <code>null</code> if no token could be retrieved
- */
-fun getAccessToken(client: OAuth2Client): OAuth2AccessToken? {
-    // Create HttpRequestExecutor
-    val ex: HttpRequestExecutor = HttpUrlConnectionExecutor()
-    // Client Credentials Grant
-    return try {
-        ClientCredentialsGrant(client, BasicScope()).accessToken(ex)
-    } catch (e: Exception) {
-        logger.error { "Could not retrieve access token with the given credentials: $e" }
-        null
-    }
-}
-
-// Currently, not implemented in the orchestrator
-/**
- * Refreshes an expiring access-token
- * @author Robert Haimerl
- * @param client an OAuth2Client containing the corresponding URL and credentials
- * @param oldToken the token which should be replaced
- * @return a new OAuth2AccessToken or null if no token could be retrieved
- */
-fun refreshToken(client: OAuth2Client, oldToken: OAuth2AccessToken): OAuth2AccessToken? {
-    // Create HttpRequestExecutor
-    val ex: HttpRequestExecutor = HttpUrlConnectionExecutor()
-    // Request new access token, providing the previous one
-    return try {
-        TokenRefreshGrant(client, oldToken).accessToken(ex)
-    } catch (e: Exception) {
-        logger.error { "Could not refresh the access token: $e" }
-        null
-    }
-}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/ApiManager.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/ApiManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..17d663a1dddc4441d150e5f80d735e0dc0b0ec4e
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/ApiManager.kt
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.connection
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.net.InetAddress
+import java.net.URL
+import java.util.UUID
+import org.openapitools.client.evidence.api.EvidenceStoreApi
+import org.openapitools.client.evidence.model.Evidence
+import org.openapitools.client.orchestrator.ApiException
+import org.openapitools.client.orchestrator.api.OrchestratorApi
+import org.openapitools.client.orchestrator.model.AssessmentResult
+import org.openapitools.client.orchestrator.model.CloudService
+
+/**
+ * The ApiManager handles calls towards the orchestrator and evidence api
+ *
+ * @param connection the connection containing this manager
+ * @param orchestratorURL the URL of the Orchestrator instance
+ * @author Robert Haimerl
+ */
+class ApiManager(private val connection: Connection, private val orchestratorURL: String) {
+    private val logger = KotlinLogging.logger {}
+    private val orchestratorApi =
+        OrchestratorApi(
+            org.openapitools.client.orchestrator.ApiClient().setBasePath(orchestratorURL)
+        )
+    private val evidenceApi =
+        EvidenceStoreApi(org.openapitools.client.evidence.ApiClient().setBasePath(orchestratorURL))
+
+    init {
+        orchestratorApi.customBaseUrl = orchestratorURL
+        evidenceApi.customBaseUrl = orchestratorURL
+    }
+
+    /**
+     * Test the connection to the Orchestrator-Server
+     *
+     * @return whether a connection could be established
+     * @author Robert Haimerl
+     */
+    fun testOrchestratorConnection(): Boolean {
+        logger.info { "Testing the connection to the Orchestrator" }
+        // first ping the server
+        val orchestratorHost = URL(orchestratorURL).host
+        if (!connection.isReachable(InetAddress.getByName(orchestratorHost), 80, 2000)) {
+            logger.error { "Failed to ping orchestrator" }
+            return false
+        }
+        // then try to make a apply the Token
+        return connection
+            .getTokenManager()
+            .applyToken(orchestratorApi.apiClient, evidenceApi.apiClient)
+    }
+
+    /**
+     * Sends the AssessmentResults to the orchestrator
+     *
+     * @param ar the AssessmentResults to send
+     * @param retryOnError whether it should retry the process on a connection error
+     * @return whether sending was successful
+     * @author Robert Haimerl
+     */
+    fun sendAssessmentResults(ar: Array<AssessmentResult>?, retryOnError: Boolean = true): Boolean {
+        if (ar.isNullOrEmpty()) {
+            logger.warn { "No AssessmentResults to send" }
+            return true
+        }
+        var nextElem = 0
+        try {
+            logger.info {
+                "trying to send AssessmentResults to ${orchestratorApi.apiClient.basePath}"
+            }
+            for (result in ar) {
+                orchestratorApi.orchestratorStoreAssessmentResult(result)
+                nextElem++
+                logger.debug { "Sent assessment result with id " + result.id }
+            }
+        } catch (ae: org.openapitools.client.orchestrator.ApiException) {
+            logger.error(ae) {
+                "Could not send to ${orchestratorApi.apiClient.basePath}: ${ae.localizedMessage}"
+            }
+            return if (retryOnError && ae.code == 401) {
+                logger.info { "Retrying transmission process..." }
+                // retries the transmission process starting from the failed element
+                sendAssessmentResults(ar.copyOfRange(nextElem, ar.size), false)
+            } else false
+        }
+        logger.info { "All assessment results sent to ${orchestratorApi.apiClient.basePath}" }
+        return true
+    }
+    /**
+     * Stores the Evidence in the Orchestrator
+     *
+     * @param evidence the Evidence to store
+     * @return whether the Evidence could be successfully stored
+     * @author Robert Haimerl
+     */
+    fun storeEvidence(evidence: Evidence): Boolean {
+        return try {
+            evidenceApi.evidenceStoreStoreEvidence(evidence)
+            logger.info { "Successfully stored evidence with id ${evidence.id}" }
+            true
+        } catch (e: org.openapitools.client.evidence.ApiException) {
+            logger.error(e) {
+                "Failed to store evidence with id ${evidence.id}: ${e.localizedMessage}"
+            }
+            false
+        }
+    }
+
+    /**
+     * Tries to get a CloudService defined by its id
+     *
+     * @param id The unique UUID of the cloud service
+     * @return The CloudService defined by the provided id
+     * @author Robert Haimerl
+     */
+    fun getCloudServiceById(id: UUID): CloudService? {
+        return try {
+            orchestratorApi.orchestratorGetCloudService(id.toString())
+        } catch (ae: ApiException) {
+            logger.error(ae) { "Failed to resolve Cloud Service Id ($id): ${ae.localizedMessage}" }
+            null
+        }
+    }
+
+    fun getOrchestratorApi(): OrchestratorApi {
+        return orchestratorApi
+    }
+
+    fun getEvidenceApi(): EvidenceStoreApi {
+        return evidenceApi
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/Connection.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/Connection.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fecb41eb39ceae5f2ddbfefacf7b9efb79047e03
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/Connection.kt
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.connection
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.net.InetAddress
+import java.net.InetSocketAddress
+import java.net.Socket
+
+class Connection(orchestratorURL: String, tokenURL: String, username: String, password: String) {
+    private val logger = KotlinLogging.logger {}
+    private val apiManager: ApiManager = ApiManager(this, orchestratorURL)
+    private val oAuthManager: OAuthManager = OAuthManager(this, tokenURL, username, password)
+    private val tokenManager: TokenManager = TokenManager(this)
+
+    /**
+     * Checks if the given address/port combination is reachable. Used since some hosts do not
+     * respond to regular isReachable on port 7
+     *
+     * @param address the INetAddress we try to ping
+     * @param port the according port
+     * @param timeout when every attempt should time out (a maximum of two connection attempts are
+     *   made)
+     * @return whether the address could be reached
+     * @author Robert Haimerl
+     */
+    @Suppress("SameParameterValue")
+    fun isReachable(address: InetAddress, port: Int = 0, timeout: Int = 2000): Boolean {
+        logger.debug { "Testing whether $address:$port is reachable" }
+        // first try default isReachable (which tries ICMP or port 7)
+        if (!address.isReachable(timeout)) {
+            logger.debug { "Could not reach $address:$port, manually retrying" }
+            // otherwise, try manual connection to specified port
+            if (port < 0 || port > 65535) return false
+            val sockAddress = InetSocketAddress(address, port)
+            val sock = Socket()
+            try {
+                sock.connect(sockAddress, timeout)
+            } catch (exc: Exception) {
+                logger.warn { "Address $address:$port is unreachable" }
+                return false
+            }
+        }
+        logger.info { "Address $address:$port can be reached" }
+        return true
+    }
+
+    fun getApiManager(): ApiManager {
+        return this.apiManager
+    }
+
+    fun getTokenManager(): TokenManager {
+        return this.tokenManager
+    }
+
+    fun getOAuthManager(): OAuthManager {
+        return this.oAuthManager
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/OAuthManager.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/OAuthManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f8953fec181cf7da805c90097a2d00159e3ed489
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/OAuthManager.kt
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.connection
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.net.InetAddress
+import java.net.URI
+import java.net.URL
+import org.dmfs.oauth2.client.*
+import org.dmfs.rfc3986.uris.EmptyUri
+import org.dmfs.rfc5545.Duration
+
+/**
+ * The OAuthManager handles the authentication process. Closely intertwined with the TokenManager
+ *
+ * @param connection the connection containing this manager
+ * @param tokenURL the URL of the OAuth2 instance
+ * @param username the OAuth2 username
+ * @param password the OAuth2 password
+ * @author Robert Haimerl
+ */
+class OAuthManager(
+    private val connection: Connection,
+    private val tokenURL: String,
+    username: String,
+    password: String
+) {
+    private val logger = KotlinLogging.logger {}
+    // Creates the authClient instantly after creating the TokenManager
+    private val authClient = createOAuth2Client(tokenURL, username, password)
+
+    /**
+     * Test the connection to the OAuth2-Servers
+     *
+     * @return whether a connection could be established
+     * @author Robert Haimerl
+     */
+    fun testOAuthConnection(): Boolean {
+        logger.info { "Testing the connection to the OAuth-Server" }
+        // fist do a simple ping
+        val tokenHost = URL(tokenURL).host
+        if (!connection.isReachable(InetAddress.getByName(tokenHost), 80, 2000)) {
+            logger.error { "Failed to ping OAuth-Server" }
+            return false
+        }
+        // then try to request a token with the given credentials
+        if (connection.getTokenManager().requestToken(authClient) == null) {
+            logger.error { "Failed to retrieve a token with the given credentials" }
+            return false
+        }
+        return true
+    }
+
+    /**
+     * Creates an OAuth2Client using the URL, username and password. The default Token ttl is set to
+     * 10 hours
+     *
+     * @param tokenURL the URL of the token-granting server
+     * @param username the username used for authentication
+     * @param password the password used for authentication
+     * @return an OAuth2Client used to request tokens
+     * @author Florian Wendland
+     * @author Robert Haimerl
+     */
+    private fun createOAuth2Client(
+        tokenURL: String,
+        username: String,
+        password: String
+    ): OAuth2Client {
+        // create OAuth2 provider
+        val pv: OAuth2AuthorizationProvider =
+            BasicOAuth2AuthorizationProvider(
+                null,
+                URI.create(tokenURL),
+                Duration(1, 0, 36000) // token expires after 10 hours
+            )
+        // Create OAuth2 client credentials
+        val cred: OAuth2ClientCredentials = BasicOAuth2ClientCredentials(username, password)
+        // Create OAuth2 client
+        val client = BasicOAuth2Client(pv, cred, EmptyUri())
+        logger.info { "Successfully created the OAuth2-Client" }
+        return client
+    }
+
+    fun getAuthClient(): OAuth2Client {
+        return this.authClient
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/TokenManager.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/TokenManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f2b0546893fca32c5ecea4fa4d29e707d62ee368
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/TokenManager.kt
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.connection
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import org.dmfs.httpessentials.client.HttpRequestExecutor
+import org.dmfs.httpessentials.httpurlconnection.HttpUrlConnectionExecutor
+import org.dmfs.oauth2.client.OAuth2AccessToken
+import org.dmfs.oauth2.client.OAuth2Client
+import org.dmfs.oauth2.client.grants.ClientCredentialsGrant
+import org.dmfs.oauth2.client.scope.BasicScope
+
+/**
+ * the OAuthManager handles the token necessary for the authentication process
+ *
+ * @param connection the connection containing this manager
+ * @author Robert Haimerl
+ */
+class TokenManager(private val connection: Connection) {
+    private val logger = KotlinLogging.logger {}
+    // Fetches a token instantly after creating the TokenManager
+    private var token: OAuth2AccessToken? =
+        requestToken(connection.getOAuthManager().getAuthClient())
+
+    init {
+        if (
+            !applyToken(
+                connection.getApiManager().getOrchestratorApi().apiClient,
+                connection.getApiManager().getEvidenceApi().apiClient,
+                token
+            )
+        ) {
+            logger.error {
+                "Failed to apply the OAuth2 access token while initializing the TokenManager"
+            }
+        }
+    }
+
+    /**
+     * Requests a new access-token
+     *
+     * @param client an OAuth2Client containing the corresponding URL and credentials
+     * @return an OAuth2AccessToken or <code>null</code> if no token could be retrieved
+     * @author Robert Haimerl
+     */
+    fun requestToken(client: OAuth2Client): OAuth2AccessToken? {
+        // Create HttpRequestExecutor
+        val ex: HttpRequestExecutor = HttpUrlConnectionExecutor()
+        // Client Credentials Grant
+        val token =
+            try {
+                ClientCredentialsGrant(client, BasicScope()).accessToken(ex)
+            } catch (e: Exception) {
+                logger.error(e) {
+                    "Could not retrieve access token with the given credentials: ${e.localizedMessage}"
+                }
+                null
+            }
+        logger.info { "Successfully requested an access token for the OAuth2-Connection" }
+        return token
+    }
+
+    /**
+     * applies the OAuth2-Token to both the Orchestrator Client and the Evidence Client
+     *
+     * @return whether there was a token that could be applied
+     * @author Robert Haimerl
+     */
+    fun applyToken(
+        orchestratorClient: org.openapitools.client.orchestrator.ApiClient,
+        evidenceClient: org.openapitools.client.evidence.ApiClient,
+        token: OAuth2AccessToken? = this.token
+    ): Boolean {
+        val tk =
+            token
+                ?: run {
+                    logger.warn { "Tried to apply the OAuth2 access token before it was available" }
+                    return false
+                }
+        orchestratorClient.addDefaultHeader(
+            "Authorization",
+            "${tk.tokenType()} ${tk.accessToken()}"
+        )
+        evidenceClient.addDefaultHeader("Authorization", "${tk.tokenType()} ${tk.accessToken()}")
+        logger.info { "Access token successfully applied to the connection" }
+        return true
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/EvaluationResult.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/EvaluationResult.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e327a2c5ee8a36f77b034bba72fa811864dbbd0a
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/EvaluationResult.kt
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation
+
+/**
+ * The result of the evaluation, its detailedMessage is being used as input for the evidence `raw`
+ */
+class EvaluationResult(
+    private val validated: Boolean,
+    private val message: String,
+    private val detailedMessage: String
+) {
+    fun isValidated(): Boolean {
+        return this.validated
+    }
+
+    fun getMessage(): String {
+        return this.message
+    }
+
+    fun getDetailedMessage(): String {
+        return this.detailedMessage
+    }
+
+    override fun toString(): String {
+        return "${if (!validated) "IN" else ""}VALID | [$message] | [$detailedMessage]"
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Evaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Evaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c214c05c35cc043274c74589d4164dec46f9005d
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Evaluator.kt
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.base.*
+import de.fraunhofer.aisec.codyze.medina.mapping.MedinaMapping
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.File
+import java.lang.IllegalArgumentException
+import java.nio.file.Path
+import kotlin.reflect.KClass
+import kotlin.reflect.full.primaryConstructor
+
+class Evaluator(private val environment: Environment, private val base: Path) {
+    /** A mapping from the rule to its respective MetricEvaluator class */
+    private val rulesToEvaluators: Map<Rule, KClass<out MetricEvaluator>> =
+        mapOf(
+            Rule.CodeSignoff to SignOffEvaluator::class,
+            Rule.SignedCommits to GPGSignatureEvaluator::class,
+            Rule.SignedSignoff to SignedSignOffEvaluator::class,
+            Rule.ApprovedCommitAuthor to ApprovedCommitAuthorEvaluator::class,
+        )
+
+    private val logger = KotlinLogging.logger {}
+
+    /**
+     * Iterates over all metrics defined in the Medina Mapping and tries to evaluate them. When
+     * found metrics cannot be evaluated they are ignored and a matching log message is created
+     *
+     * @param medinaMapping the Medina Mapping as parsed from the respective file
+     * @return A map mapping each evaluated rule to its evaluation result
+     * @author Robert Haimerl
+     */
+    fun evaluateAll(medinaMapping: MedinaMapping): Map<Rule, EvaluationResult> {
+        logger.info { "Starting Evaluation of MEDINA Metrics" }
+        val results = mutableMapOf<Rule, EvaluationResult>()
+        for (metric in medinaMapping.metrics) {
+            try {
+                val rule = Rule.valueOf(metric.name)
+                logger.debug { "Starting Evaluation of Metric ${metric.name}" }
+                // Return custom error message if git hash could not be resolved
+                if (environment.getGitHash() == "invalid") {
+                    results[rule] =
+                        EvaluationResult(
+                            false,
+                            "Git commit hash could not be resolved",
+                            "Git commit hash could not be resolved"
+                        )
+                } else {
+                    // call the evaluation function for the corresponding rule and pass the metric
+                    // configuration
+                    results[rule] =
+                        rulesToEvaluators[rule]!!
+                            .primaryConstructor!!
+                            .call(environment, base)
+                            .evaluate(metric.target)
+                }
+            } catch (ie: IllegalArgumentException) {
+                logger.warn { "No Rule for ${metric.name} could be found" }
+            } catch (npe: NullPointerException) {
+                logger.error { "No evaluation function for ${metric.name} was defined" }
+            }
+        }
+        return results
+    }
+
+    /**
+     * Tries to import all files at the specified location into GPG as public keys
+     *
+     * @param keyLocation A PubKey-File or Directory containing public keys
+     * @author Robert Haimerl
+     */
+    fun parseKeys(keyLocation: File) {
+        logger.info { "Trying to import public keys from ${keyLocation.path}" }
+        val files =
+            if (keyLocation.isDirectory) {
+                collectFiles(keyLocation)
+            } else {
+                setOf(keyLocation)
+            }
+        var success = 0
+        for (key in files) {
+            val process =
+                Runtime.getRuntime()
+                    .exec(arrayOf("gpg", "--import", key.canonicalPath), null, base.toFile())
+            process.waitFor()
+            if (process.exitValue() != 0) {
+                logger.warn { "Failed to import public key file ${key.name}" }
+            } else {
+                success += 1
+            }
+        }
+        logger.info { "Successfully registered $success keys" }
+    }
+
+    /**
+     * Recursively collects all regular files within a directory into a set
+     *
+     * @param directory The starting directory
+     * @return A set of all regular files contained in the directory and all subdirectories
+     * @author Robert Haimerl
+     */
+    private fun collectFiles(directory: File): Set<File> {
+        val files = mutableSetOf<File>()
+
+        for (f in directory.walk()) {
+            if (f != directory && f.isDirectory) {
+                files += collectFiles(f)
+            } else if (f.isFile) {
+                files += f
+            }
+        }
+        return files
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Rule.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Rule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1cd693234f5a98cf2836f823f71cbf91940b40d9
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Rule.kt
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation
+
+/**
+ * The rules that can be evaluated. Each rule contains information about its associated operator and
+ * target type
+ *
+ * @param operator The operator used for evaluating the result of the rule
+ * @param targetValue The desired evaluation result
+ * @param id The stable and unique id of this rule, used for SARIF reports
+ * @param description A description of the rule, used for SARIF reports
+ */
+enum class Rule(
+    val operator: String,
+    val targetValue: Any,
+    val id: String,
+    val description: String
+) {
+    CodeSignoff(
+        "==",
+        true,
+        "MD001",
+        "Whether the analyzed commit was signed off by a pre-specified subject"
+    ),
+    SignedCommits(
+        "==",
+        true,
+        "MD002",
+        "Whether the analyzed commit contains a valid signature from a pre-specified subject"
+    ),
+    SignedSignoff(
+        "==",
+        true,
+        "MD003",
+        "Whether the analyzed commit contains both a signoff and a valid signature from a pre-specified subject"
+    ),
+    ApprovedCommitAuthor(
+        "==",
+        true,
+        "MD004",
+        "Whether the analyzed commit comes from a permitted author"
+    ),
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/ApprovedCommitAuthorEvaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/ApprovedCommitAuthorEvaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..797adc84106fbf10237e02f7bb5c4e6c0d81e63a
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/ApprovedCommitAuthorEvaluator.kt
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation.base
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import io.github.oshai.kotlinlogging.KLogger
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.nio.file.Path
+
+class ApprovedCommitAuthorEvaluator(
+    override val environment: Environment,
+    override val base: Path
+) : MetricEvaluator() {
+
+    override val logger: KLogger = KotlinLogging.logger {}
+
+    /**
+     * Validates commit author against list of approved authors.
+     *
+     * @return true if author is approved; otherwise, false
+     * @author Florian Wendland
+     */
+    override fun evaluate(target: Array<Any>): EvaluationResult {
+        val gitHash = environment.getGitHash()
+        val commitAuthor = getCommitAuthorName(gitHash)
+
+        if (target.contains<Any?>(commitAuthor)) {
+            return EvaluationResult(
+                true,
+                "Commit from approved author: ${commitAuthor}",
+                "Commit from approved author: ${commitAuthor}, expected one of: ${target.contentToString()}"
+            )
+        }
+        return EvaluationResult(
+            false,
+            "Commit from unapproved author: ${commitAuthor}",
+            "Commit from unapproved author: ${commitAuthor}, expected one of: ${target.contentToString()}"
+        )
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/GPGSignatureEvaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/GPGSignatureEvaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..83d5b4d4761d86d4b25580fe0fe468f3f87c4414
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/GPGSignatureEvaluator.kt
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation.base
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import io.github.oshai.kotlinlogging.KLogger
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.nio.file.Path
+
+class GPGSignatureEvaluator(override val environment: Environment, override val base: Path) :
+    MetricEvaluator() {
+
+    override val logger: KLogger = KotlinLogging.logger {}
+
+    override fun evaluate(target: Array<Any>): EvaluationResult {
+        val goodSignatures =
+            getGoodSignatures(getCommitSignatureInformation(environment.getGitHash()))
+        if (goodSignatures.isEmpty()) {
+            return EvaluationResult(
+                false,
+                "The commit contains no good signatures",
+                "was: [], expected one of: ${target.contentToString()}"
+            )
+        }
+        for (signature in goodSignatures) {
+            if (target.contains(signature.first)) {
+                return EvaluationResult(
+                    true,
+                    "Commit signed by ${signature.second} <${signature.third}> with the key id ${signature.first}",
+                    "was: ${goodSignatures.map { it.first }}, expected one of: ${target.contentToString()}"
+                )
+            }
+        }
+        return EvaluationResult(
+            false,
+            "The commit contains good signatures, but none match the specified key ids",
+            "was: ${goodSignatures.map { it.first }}, expected one of: ${target.contentToString()}"
+        )
+    }
+
+    /**
+     * Tries to find good signatures in a gnupg status output as returned by
+     * *getCommitSignatureInformation*
+     *
+     * @param gpgStatus The gnupg status output like it is produced when calling git
+     *   verify-signature
+     * @return A List of Triple<KeyId, Name, Email> that describes valid signatures of the commit
+     * @author Robert Haimerl
+     */
+    internal fun getGoodSignatures(gpgStatus: List<String>): List<Triple<String, String, String>> {
+        return gpgStatus
+            .filter { line -> line.startsWith("[GNUPG:] GOODSIG") }
+            .map { line -> splitGoodSignatureLine(line) }
+            .toList()
+    }
+
+    /**
+     * Splits a GOODSIG line of the gnupg status output, undefined return value for other argument
+     * Strings When an Exception occurs while trying to parse the keyId, it will be stored as -1
+     *
+     * @param line The line of the output that shall be split
+     * @return The Triple<KeyId, Name, Email>
+     * @author Robert Haimerl
+     */
+    private fun splitGoodSignatureLine(line: String): Triple<String, String, String> {
+        val splitLine = line.split(" ")
+        if (splitLine.size < 4) {
+            logger.error { "Signature line is too short to be parsed properly" }
+            return Triple("", "", "")
+        }
+        val keyId = splitLine[2]
+        val name = splitLine.subList(3, splitLine.size - 1).joinToString(" ")
+        val email = splitLine.last().substring(1, splitLine.last().length - 1)
+        return Triple(keyId, name, email)
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/MetricEvaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/MetricEvaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c681cd7179aa080188540a05bcd02f25a1853adb
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/MetricEvaluator.kt
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation.base
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import io.github.oshai.kotlinlogging.KLogger
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.nio.file.Path
+
+abstract class MetricEvaluator {
+
+    abstract val environment: Environment
+    abstract val base: Path
+    abstract val logger: KLogger
+
+    /**
+     * Abstract evaluation function to be overwritten by each specific Evaluator
+     *
+     * @param target The target values for this evaluation
+     * @return An EvaluationResult containing a message and whether the metric could be validated
+     */
+    abstract fun evaluate(target: Array<Any>): EvaluationResult
+
+    /**
+     * Fetches the name of the commit author from the git console commands
+     *
+     * @return The full author name or null if it could not be found
+     * @author Robert Haimerl
+     */
+    internal fun getCommitAuthorName(commitHash: String): String {
+        val process =
+            Runtime.getRuntime().exec("git show -s --format=%an $commitHash", null, base.toFile())
+        val reader = BufferedReader(InputStreamReader(process.inputStream))
+        return reader.readLine()
+    }
+
+    /**
+     * Fetches the name of the commit author from the git console commands
+     *
+     * @return The full author name or null if it could not be found
+     * @author Robert Haimerl
+     */
+    internal fun getCommitAuthorEmail(commitHash: String): String {
+        val process =
+            Runtime.getRuntime().exec("git show -s --format=%ae $commitHash", null, base.toFile())
+        val reader = BufferedReader(InputStreamReader(process.inputStream))
+        return reader.readLine()
+    }
+
+    /**
+     * Searches the commit in question for sign-offs. A sign-off is identified by verifying whether
+     * the last line starts with the phrase "Signed-off-by: "
+     *
+     * @return A List of the found sign-off lines, may be empty
+     * @author Robert Haimerl
+     */
+    internal fun getSignOff(commitHash: String): List<String> {
+        // try to read the commit message as console command
+        val process =
+            Runtime.getRuntime()
+                .exec("git show --pretty=format:\"%B\" --no-patch $commitHash", null, base.toFile())
+        val commitMessageLines =
+            BufferedReader(InputStreamReader(process.inputStream)).readLines().toMutableList()
+        val signOffList: MutableList<String> = mutableListOf()
+        for (line in commitMessageLines) {
+            if (line.startsWith("Signed-off-by: ")) {
+                signOffList += line
+            }
+        }
+        return signOffList
+    }
+
+    /**
+     * Calls git verify-commit raw and returns the response as a list of the separate lines of the
+     * response
+     *
+     * @param commitHash The commit of which we want to check the signature
+     * @return The separate lines of a machine-readable gpg status output, may be empty
+     * @author Robert Haimerl
+     */
+    internal fun getCommitSignatureInformation(commitHash: String): List<String> {
+        val process =
+            Runtime.getRuntime().exec("git verify-commit --raw $commitHash", null, base.toFile())
+        return BufferedReader(InputStreamReader(process.errorStream)).readLines().toList()
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignOffEvaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignOffEvaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2c8e0f0a02dfe580231f49727510a7103e4d6755
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignOffEvaluator.kt
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation.base
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import io.github.oshai.kotlinlogging.KLogger
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.nio.file.Path
+
+class SignOffEvaluator(override val environment: Environment, override val base: Path) :
+    MetricEvaluator() {
+
+    override val logger: KLogger = KotlinLogging.logger {}
+
+    /**
+     * Tries to get all sign-offs of the current commit and compares it to the name and email
+     * associated with the commit author
+     *
+     * @return True only if the commit contains a sign-off which matches the expected author
+     * @author Robert Haimerl
+     */
+    override fun evaluate(target: Array<Any>): EvaluationResult {
+        val gitHash = environment.getGitHash()
+        val signOffList = getSignOff(gitHash).map { parseSignOff(it) }
+        for (signOff in signOffList) {
+            if (
+                signOff.first == getCommitAuthorName(gitHash) &&
+                    signOff.second == getCommitAuthorEmail(gitHash) &&
+                    target.contains(signOff.first)
+            )
+                return EvaluationResult(
+                    true,
+                    "Message contains a sign-off by ${signOff.first}",
+                    "was: ${signOffList.map { it.first }}, expected one of: ${target.contentToString()}"
+                )
+        }
+        return EvaluationResult(
+            false,
+            "Message is not signed off by any of the specified signers",
+            "was: ${signOffList.map { it.first }}, expected one of: ${target.contentToString()}"
+        )
+    }
+
+    /**
+     * Parses a sign-off line into a name and email
+     *
+     * @return The Pair <Name, Email>
+     * @author Robert Haimerl
+     */
+    internal fun parseSignOff(signOff: String): Pair<String, String> {
+        val signOffParts = signOff.split(" ")
+        val signOffName =
+            signOffParts.subList(1, signOffParts.size - 1).joinToString(separator = " ")
+        val signOffEmail = signOffParts.last().substring(1, signOffParts.last().length - 1)
+        return Pair(signOffName, signOffEmail)
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignedSignOffEvaluator.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignedSignOffEvaluator.kt
new file mode 100644
index 0000000000000000000000000000000000000000..651d9f9b7d66da16d77eed7508b6fce8eeb9da1e
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignedSignOffEvaluator.kt
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.evaluation.base
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import io.github.oshai.kotlinlogging.KLogger
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.nio.file.Path
+
+class SignedSignOffEvaluator(override val environment: Environment, override val base: Path) :
+    MetricEvaluator() {
+
+    override val logger: KLogger = KotlinLogging.logger {}
+
+    private val signOffEvaluator: SignOffEvaluator = SignOffEvaluator(environment, base)
+    private val gpgSignatureEvaluator: GPGSignatureEvaluator =
+        GPGSignatureEvaluator(environment, base)
+
+    /**
+     * This evaluator expects the target to be a map with the following keys:
+     * - name
+     * - email
+     * - pub-key-id
+     */
+    @Suppress("UNCHECKED_CAST")
+    override fun evaluate(target: Array<Any>): EvaluationResult {
+        // NOTE: we can not just check whether both the Signature and the SignOff evaluate correctly
+        // since both can be from different sources which is not enough for this evaluation.
+        // To avoid code duplication we still re-use the functionality provided by those two
+        // Evaluators
+        val gitCommitHash = environment.getGitHash()
+        val goodSignatures =
+            gpgSignatureEvaluator.getGoodSignatures(
+                gpgSignatureEvaluator.getCommitSignatureInformation(gitCommitHash)
+            )
+        val signOffs = signOffEvaluator.getSignOff(gitCommitHash)
+
+        // Try to cast the target values into maps
+        val targetMaps: Array<LinkedHashMap<String, String>>
+        try {
+            targetMaps = target.map { t -> t as LinkedHashMap<String, String> }.toTypedArray()
+        } catch (e: ClassCastException) {
+            logger.error {
+                "The target for \"SignedSignoff\" is not correctly formatted. Expected an array of maps:\n\ttarget:\n\t\t- name: ...\n\t\t  email: ...\n\t\t  pub-key-id: ..."
+            }
+            return EvaluationResult(
+                false,
+                "Metric target was not correctly formatted",
+                "was: ${target.javaClass.simpleName}, Expected an array of maps"
+            )
+        }
+
+        // Compare the results with the specified targets for this metric by iterating over all
+        // valid gpg signatures
+        for (signature in goodSignatures) {
+            // First check whether a sign-off matches the signature
+            for (signOff in signOffs) {
+                val signOffTuple = signOffEvaluator.parseSignOff(signOff)
+                if (
+                    signOffTuple.first == signature.second && signOffTuple.second == signature.third
+                ) {
+                    // Then check whether this signer is specified as one of the targets in the
+                    // configuration
+                    for (map in targetMaps) {
+                        if (
+                            map["name"] == signOffTuple.first &&
+                                map["email"] == signOffTuple.second &&
+                                map["pub-key-id"] == signature.first
+                        ) {
+                            // Finally, check whether this matches the git commit author
+                            if (
+                                signOffEvaluator.getCommitAuthorName(gitCommitHash) ==
+                                    map["name"] &&
+                                    signOffEvaluator.getCommitAuthorEmail(gitCommitHash) ==
+                                        map["email"]
+                            ) {
+                                return EvaluationResult(
+                                    true,
+                                    "The commit was signed off and contains a good signature from ${map["name"]} <${map["email"]}> with the key id ${map["pub-key-id"]}",
+                                    "was: ${goodSignatures.map { it.second }}, expected one of: ${targetMaps.map { it["name"] }}"
+                                )
+                            }
+                            return EvaluationResult(
+                                false,
+                                "The commit was signed off and contains a good signature, but the signer does not match the commit author",
+                                "was: ${goodSignatures.map { it.second }}, expected one of: ${targetMaps.map { it["name"] }}"
+                            )
+                        }
+                    }
+                    return EvaluationResult(
+                        false,
+                        "The commit was signed off and contains a good signature, but the signer is not specified as a valid target",
+                        "was: ${goodSignatures.map { it.second }}, expected one of: ${targetMaps.map { it["name"] }}"
+                    )
+                }
+                return EvaluationResult(
+                    false,
+                    "The commit contains a good signature, but matching sign-off could be found",
+                    "was: ${goodSignatures.map { it.second }}, expected one of: ${targetMaps.map { it["name"] }}"
+                )
+            }
+            return EvaluationResult(
+                false,
+                "The commit contains no sign-off",
+                "was: ${signOffs}, expected one of: ${targetMaps.map { it["name"] }}"
+            )
+        }
+        return EvaluationResult(
+            false,
+            "The commit contains no good GPG signature",
+            "was: ${goodSignatures.map { it.second }}, expected one of: ${targetMaps.map { it["name"] }}"
+        )
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/CodyzeMedina.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/CodyzeMedina.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9d4e363176c22259c2d052b62264eb8585424acd
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/CodyzeMedina.kt
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.main
+
+import de.fraunhofer.aisec.codyze.analysis.AnalysisServer
+import de.fraunhofer.aisec.codyze.analysis.Finding
+import de.fraunhofer.aisec.codyze.medina.assembling.Assembler
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.connection.Connection
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Evaluator
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
+import de.fraunhofer.aisec.codyze.medina.mapping.Mapping
+import de.fraunhofer.aisec.codyze.medina.mapping.parseMappingTree
+import de.fraunhofer.aisec.codyze.medina.mapping.parseMedinaMapping
+import de.fraunhofer.aisec.codyze.medina.util.*
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.File
+import java.io.IOException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.StandardCopyOption
+import java.util.*
+import java.util.concurrent.Callable
+import java.util.concurrent.TimeUnit
+import kotlin.io.path.absolute
+import kotlin.system.exitProcess
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.LoggerContext
+import org.openapitools.client.orchestrator.ApiException
+import picocli.CommandLine
+import picocli.CommandLine.Command
+import picocli.CommandLine.Mixin
+
+@Command(
+    name = "codyze-medina",
+    version = ["Codyze for MEDINA ${CodyzeMedina.CODYZE_VERSION}"],
+    mixinStandardHelpOptions = true
+)
+class CodyzeMedina(config: Configuration, args: Array<String>) : Callable<Int> {
+    private val logger = KotlinLogging.logger {}
+    @Mixin val configuration: Configuration = config
+    private val cmdlineArgs = args
+
+    /**
+     * Callable function containing the core program logic
+     *
+     * @return A code specified by the function handleReturn()
+     * @author Florian Wendland
+     * @author Robert Haimerl
+     */
+    override fun call(): Int {
+        // Check if all necessary options are set
+        configuration.validate()
+        val basePath = configuration.configFile.path.absolute().parent
+
+        // Prepare the connection to the Orchestrator
+        val connection =
+            Connection(
+                configuration.orchestrator.orchestratorEndpoint.toString(),
+                configuration.orchestrator.auth.oauthEndpoint.toString(),
+                configuration.orchestrator.auth.username,
+                configuration.orchestrator.auth.password
+            )
+
+        // Prepare the configuration for Codyze
+        val config =
+            de.fraunhofer.aisec.codyze.config.Configuration.initConfig(
+                configuration.configFile.path.toFile(),
+                *cmdlineArgs,
+                "--default-passes",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.IdentifierPass",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.EdgeCachePass"
+            )
+
+        // do not allow LSP/TUI
+        if (config.executionMode.isLsp || config.executionMode.isTui) {
+            logger.warn { "Forbidden execution mode, changing to CLI" }
+            config.executionMode.isCli = true
+            config.executionMode.isLsp = false
+            config.executionMode.isTui = false
+        }
+
+        // overwrite the mark file path with our resolved locations
+        var cc = config.getCodyzeConfiguration()
+        cc.mark =
+            convertMarkConfig(
+                    configuration.mark.builtinMark,
+                    configuration.mark.projectMark,
+                    basePath
+                )
+                .map { string -> File(string) }
+                .toTypedArray()
+
+        // overwrite `no-good-findings` in Codyze configuration to always return all results
+        cc.noGoodFindings = false
+
+        config.setCodyzeConfiguration(cc)
+
+        // Create necessary helper objects
+        val environment = Environment(configuration.ci, basePath)
+        val assembler = Assembler(environment, configuration.id)
+        val evaluator = Evaluator(environment, basePath)
+        var analysisFailure = false
+        var numberOfNegativeFindings = 0
+        var connectionError = false
+        val gitCommitHash = environment.getGitHash()
+
+        // Check whether the CloudServiceId is recognized
+        val validUUID =
+            try {
+                val uuid = UUID.fromString(configuration.id)
+                val cloudService = connection.getApiManager().getCloudServiceById(uuid)
+                logger.info {
+                    "Found ${cloudService?.name ?: "nothing"} when looking up the cloud service id"
+                }
+                cloudService != null
+            } catch (e: IllegalArgumentException) {
+                connectionError = true
+                logger.error {
+                    "Failed parsing orchestrator response when checking the service id. Make sure the API is up to date"
+                }
+                false
+            } catch (e: ApiException) {
+                // error code 404 -> cloud service not found, otherwise connection failed
+                if (e.code != 404) {
+                    logger.warn {
+                        "Connection to the orchestrator failed while trying to look up the cloud service id"
+                    }
+                    connectionError = true
+                } else {
+                    logger.warn { "Cloud service id could not be found" }
+                }
+                false
+            }
+
+        // Evaluate the rules defined in the Medina.yaml
+        val medinaMapping =
+            parseMedinaMapping(relativeLocation(configuration.rules.toFile(), basePath))
+        var evaluationResults: Map<Rule, EvaluationResult> = mapOf()
+        if (medinaMapping != null) {
+            evaluator.parseKeys(relativeLocation(configuration.keyLocation.toFile(), basePath))
+            evaluationResults = evaluator.evaluateAll(medinaMapping)
+            if (evaluationResults.filter { result -> !result.value.isValidated() }.isNotEmpty())
+                analysisFailure = true
+        }
+
+        val mappingsToFindings = mutableMapOf<Mapping, Set<Finding>>()
+        val codyzeMarkConfig = config.getCodyzeConfiguration().mark
+        val sources = config.source.map { relativeLocation(it, basePath) }.toTypedArray()
+
+        // Parse the Mapping for each mark path contained in the configuration
+        for (i in codyzeMarkConfig.indices) {
+            val markDirectory = relativeLocation(codyzeMarkConfig[i], basePath)
+            val mappingToDirectory = parseMappingTree(markDirectory)
+            // Iterate over each mapping and start an analysis with the included rules
+            logger.info {
+                "Starting the runs for ${mappingToDirectory.size} mappings within $markDirectory"
+            }
+            for (entry in mappingToDirectory.entries) {
+                // Modify Codyze config to only include relevant Mark rules
+                cc = config.getCodyzeConfiguration()
+                cc.mark = arrayOf(entry.value)
+                config.setCodyzeConfiguration(cc)
+
+                // Start the server and generate Findings
+                val server = AnalysisServer(config)
+                server.start()
+                val ctx = server.analyze(sources)[config.timeout, TimeUnit.MINUTES]
+                val findings = ctx.findings
+
+                // Check whether any negative findings were produced
+                numberOfNegativeFindings += findings.filter { f -> f.isProblem }.size
+                // Add the new findings to the aggregate
+                mappingsToFindings[entry.key] = findings
+            }
+        }
+        analysisFailure = numberOfNegativeFindings != 0 || analysisFailure
+
+        val resultLocation = relativeLocation(configuration.resultFile.toFile(), basePath).toPath()
+        // Print result files before sending the results to the Orchestrator
+        printFindings(mappingsToFindings.values.flatten().toSet(), config)
+
+        val fallbackReport = false
+        var moveError = false
+        val resolvedOutputLocation = relativeLocation(File(config.output), basePath).canonicalPath
+        if (configuration.combinedOutput) {
+            try {
+                amendExistingReport(Path.of(config.output), resultLocation, evaluationResults)
+            } catch (e: Exception) {
+                logger.error {
+                    "Failed to amend the Codyze report at ${config.output}: ${e.localizedMessage}. Falling back to separate report"
+                }
+                printSarifReport(evaluationResults, resultLocation)
+            }
+        }
+        if (!configuration.combinedOutput || fallbackReport) {
+            // We explicitely need to move the original Codyze SARIF Output if the location was not
+            // an absolute path
+            try {
+                Files.move(
+                    Path.of(config.output),
+                    Path.of(resolvedOutputLocation),
+                    StandardCopyOption.REPLACE_EXISTING
+                )
+            } catch (e: IOException) {
+                moveError = true
+                logger.error {
+                    "Failed to move the Codyze output file from ${config.output} to the target directory $resolvedOutputLocation"
+                }
+            }
+
+            printSarifReport(evaluationResults, resultLocation)
+        }
+        val eventualCodyzeOutputLocation = if (moveError) config.output else resolvedOutputLocation
+
+        connectionError =
+            connectionError ||
+                !sendResults(
+                    mappingsToFindings,
+                    evaluationResults,
+                    assembler,
+                    environment.getGitHash(),
+                    connection,
+                    resultLocation,
+                    Path.of(eventualCodyzeOutputLocation),
+                    configuration.combinedOutput
+                )
+        createLogReport(
+            evaluationResults,
+            numberOfNegativeFindings,
+            eventualCodyzeOutputLocation,
+            resultLocation,
+            configuration.combinedOutput
+        )
+
+        // determine the return value
+        return handleReturn(
+            connectionError && configuration.orchestrator.requiredReachable,
+            !validUUID,
+            gitCommitHash == "invalid",
+            analysisFailure
+        )
+    }
+
+    companion object {
+        const val CODYZE_VERSION = "1.6.0"
+    }
+}
+
+private val logger = KotlinLogging.logger {}
+/**
+ * Entry point of the program
+ *
+ * @author Florian Wendland
+ * @author Robert Haimerl
+ * @args cli arguments provided
+ */
+fun main(args: Array<String>) {
+    // dynamically set the log level according to CODYZE_LOG_LEVEL
+    setLogLevel()
+    // initialize configuration
+    val config = Configuration.initialize(args)
+    // parse remaining CLI options and overwrite defaults and options from config file
+    val returnValue =
+        CommandLine(CodyzeMedina(config, args)).setUnmatchedArgumentsAllowed(true).execute(*args)
+    logger.info { "Exit with return value: $returnValue" }
+    exitProcess(returnValue)
+}
+
+/**
+ * Returns the appropriate code and prints an error message to the error stream if needed:
+ * - 126 when connection to Orchestrator failed
+ * - else 3 when cloud service id is not recognizable
+ * - else 127 for general application errors
+ * - else 1 when only Analysis produced negative findings
+ * - else 0
+ */
+fun handleReturn(
+    connectionError: Boolean,
+    idError: Boolean,
+    generalError: Boolean,
+    analysisFailure: Boolean
+): Int {
+    return if (connectionError) {
+        System.err.println("Connection to the orchestrator failed")
+        126
+    } else if (idError) {
+        System.err.println("Cloud service id was not recognized")
+        3
+    } else if (generalError) {
+        System.err.println(
+            "Errors while executing codyze-medina. Consult the logs for more details"
+        )
+        127
+    } else if (analysisFailure) {
+        System.err.println("Analysis completed with violations")
+        1
+    } else {
+        0
+    }
+}
+
+/**
+ * Sets the logLevel of Log4J according to the environment variable "CODYZE_LOG_LEVEL" if given This
+ * is done via an environment variable instead of a configuration parameter to be able to set this
+ * before evaluating the config file
+ */
+fun setLogLevel() {
+    // instantly return when the variable is not set, log a warning when the value could not be
+    // translated to a level
+    val envValue = System.getenv("CODYZE_LOG_LEVEL") ?: return
+    val logLevel =
+        Level.getLevel(envValue)
+            ?: run {
+                logger.warn {
+                    "The value of \"CODYZE_LOG_LEVEL\" (\"$envValue\") could not be translated to a log level"
+                }
+                return
+            }
+    // update the configuration of the root logger
+    val ctx = LogManager.getContext(false) as LoggerContext
+    val cfg = ctx.configuration
+    val loggerConfig = cfg.getLoggerConfig(LogManager.ROOT_LOGGER_NAME)
+    loggerConfig.level = logLevel
+    ctx.updateLoggers()
+}
+
+/**
+ * Creates the Evidences and AssessmentResults and sends them to the Orchestrator. This method
+ * assumes that the respective SARIF files already exist.
+ *
+ * @param findings All Findings resulting from the analysis, associated with the mapping that caused
+ *   them
+ * @param evaluationResults All EvaluationResults from evaluating MEDINA metrics
+ * @param assembler The assembler used to create Evidences and AssessmentResults
+ * @param gitHash The git hash of the analyzed project
+ * @param connection The (already established) connection to the Orchestrator
+ * @param medinaResultFile The SARIF file carrying MEDINA results
+ * @param codyzeResultFile The SARIF file carrying Codyze results
+ * @param combinedResults Whether the SARIF files were combined
+ * @return whether the upload was successful
+ */
+fun sendResults(
+    findings: Map<Mapping, Set<Finding>>,
+    evaluationResults: Map<Rule, EvaluationResult>,
+    assembler: Assembler,
+    gitHash: String,
+    connection: Connection,
+    medinaResultFile: Path,
+    codyzeResultFile: Path,
+    combinedResults: Boolean
+): Boolean {
+    var error = false
+    // start by sending MEDINA metrics
+    val mEvidence = assembler.createEvidence(medinaResultFile)
+    error = error || !connection.getApiManager().storeEvidence(mEvidence)
+    for (evResult in evaluationResults) {
+        val assessmentResult =
+            assembler.convertEvaluationToAssessmentResult(
+                evResult.key,
+                evResult.value,
+                mEvidence.id ?: "",
+                gitHash
+            )
+        error =
+            error || !connection.getApiManager().sendAssessmentResults(arrayOf(assessmentResult))
+    }
+    // then send Codyze Findings
+    val cEvidence = if (combinedResults) mEvidence else assembler.createEvidence(codyzeResultFile)
+    if (!combinedResults) error = error || !connection.getApiManager().storeEvidence(cEvidence)
+    for (entry in findings) {
+        if (entry.value.isEmpty()) {
+            continue
+        }
+        val results =
+            assembler.convertFindingsToAssessmentResults(
+                entry.key,
+                entry.value,
+                cEvidence.id ?: "",
+                gitHash
+            )
+        error = error || !connection.getApiManager().sendAssessmentResults(results)
+    }
+    return !error
+}
+
+/**
+ * Resolves a filepath relative to the location of the given base path. If the path is absolute, it
+ * does not change
+ *
+ * @param filePath The relative or absolute path of the file
+ * @param base The base path
+ * @return The same path evaluated relative to the configuration file
+ * @author Robert Haimerl
+ */
+fun relativeLocation(filePath: File, base: Path): File {
+    return base.resolve(filePath.toPath()).toFile()
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Configuration.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/Configuration.kt
similarity index 69%
rename from src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Configuration.kt
rename to src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/Configuration.kt
index 7de6b5a93c5c27b51f66f8cfe84c1da319cc028e..1c2fe528102af4c5ddcb0d422e8372c31cf60d2f 100644
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Configuration.kt
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/Configuration.kt
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 /* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,16 +26,16 @@
  * 
  * This file is part of the MEDINA Framework.
  */
-package de.fraunhofer.aisec.codyze.medina
+package de.fraunhofer.aisec.codyze.medina.main
 
 import com.fasterxml.jackson.annotation.JsonIgnore
 import com.fasterxml.jackson.annotation.JsonProperty
 import com.fasterxml.jackson.databind.DeserializationFeature
 import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
+import io.github.oshai.kotlinlogging.KotlinLogging
 import java.io.IOException
 import java.net.URL
 import java.nio.file.Path
-import mu.KotlinLogging
 import picocli.CommandLine
 import picocli.CommandLine.Mixin
 import picocli.CommandLine.Option
@@ -43,6 +43,7 @@ import picocli.CommandLine.Spec
 
 /**
  * Configuration class containing all needed variables passed in the configuration file and cli
+ *
  * @author Florian Wendland
  * @author Robert Haimerl
  */
@@ -55,28 +56,77 @@ class Configuration {
 
     @Mixin val orchestrator = Orchestrator()
 
+    @Mixin val mark = Mark()
+
     /** The currently supported CI Environments */
-    enum class Environment {
+    enum class CIEnvironment {
         NONE,
         GITLAB,
         GITHUB,
         JENKINS
     }
 
+    @JsonProperty("ci")
     @Option(
         names = ["--ci"],
-        required = false,
         paramLabel = "ENUM",
         description = ["CI environment currently being used"]
     )
-    var ci: Environment = Environment.NONE
+    var ci: CIEnvironment = CIEnvironment.NONE
+
+    @JsonProperty("id")
+    @Option(
+        names = ["--id"],
+        paramLabel = "STRING",
+        description = ["Id of the analyzed cloud service"]
+    )
+    lateinit var id: String
+
+    @JsonProperty("rules")
+    @Option(
+        names = ["--rules"],
+        paramLabel = "FILE",
+        description = ["Mapping file for Medina rules.\nDefault: 'codyze-medina-metrics.yaml'"]
+    )
+    var rules: Path = Path.of("codyze-medina-metrics.yaml")
+
+    @JsonProperty("medina-output")
+    @Option(
+        names = ["--medina-output"],
+        paramLabel = "PATH",
+        description =
+            [
+                "File where MEDINA rule evaluation will be stored\nDefault: 'codyze-medina.sarif'\nIn case `combined-output` is set to true, this is the location of the combined result file"
+            ]
+    )
+    var resultFile: Path = Path.of("codyze-medina.sarif")
+
+    @JsonProperty("combined-output")
+    @Option(
+        names = ["--combined-output"],
+        paramLabel = "BOOLEAN",
+        description =
+            [
+                "Whether the respective SARIF files created by Codyze and the MEDINA evaluation should be merged"
+            ]
+    )
+    var combinedOutput: Boolean = true
+
+    @JsonProperty("key-location")
+    @Option(
+        names = ["--key-location"],
+        paramLabel = "PATH",
+        description = ["Location of the public key files needed to evaluate GPG Signatures"]
+    )
+    var keyLocation: Path = Path.of("public-keys")
 
     class ConfigFile {
+        @JsonProperty("config")
         @Option(
             names = ["--config"],
             paramLabel = "FILE",
-            defaultValue = "codyze.yaml",
-            description = ["Configuration file for Codyze.\nDefault: 'codyze.yaml'"]
+            defaultValue = "codyze-medina.yaml",
+            description = ["Configuration file for Codyze.\nDefault: 'codyze-medina.yaml'"]
         )
         lateinit var path: Path
     }
@@ -85,6 +135,14 @@ class Configuration {
 
         @Mixin val auth = Auth()
 
+        @JsonProperty("required")
+        @Option(
+            names = ["--required"],
+            paramLabel = "BOOLEAN",
+            description = ["Whether the Program fails when the Orchestrator is not reachable"]
+        )
+        var requiredReachable: Boolean = true
+
         @JsonProperty("endpoint")
         @Option(
             names = ["--endpoint"],
@@ -131,8 +189,29 @@ class Configuration {
         fun isOrchestratorEndpointInitialized(): Boolean = ::orchestratorEndpoint.isInitialized
     }
 
+    class Mark {
+        @JsonProperty("builtin")
+        @Option(
+            names = ["--mark-builtin"],
+            paramLabel = "LIST<FILE>",
+            description = ["The builtin MARK files used to analyze the project."]
+        )
+        var builtinMark: List<String> = listOf()
+
+        @JsonProperty("project")
+        @Option(
+            names = ["--mark-project"],
+            paramLabel = "LIST<FILE>",
+            description = ["The external MARK files used to analyze the project."]
+        )
+        var projectMark: List<String> = listOf()
+    }
+
+    private fun isCloudServiceIdInitialized(): Boolean = ::id.isInitialized
+
     /**
      * Checks whether all necessary fields were initialized
+     *
      * @author Florian Wendland
      * @author Robert Haimerl
      */
@@ -161,6 +240,12 @@ class Configuration {
                 "Missing orchestrator endpoint URL ('--endpoint')"
             )
         }
+        if (!isCloudServiceIdInitialized()) {
+            throw CommandLine.ParameterException(
+                spec.commandLine(),
+                "Missing cloud service id ('--id')"
+            )
+        }
         logger.info { "Successfully validated the configuration" }
     }
 
@@ -169,9 +254,10 @@ class Configuration {
 
         /**
          * Initializes the Configuration object by parsing the configuration file
-         * @author Florian Wendland
+         *
          * @param args the command-line arguments
          * @return a fully initialized Configuration object
+         * @author Florian Wendland
          */
         fun initialize(args: Array<String>): Configuration {
             logger.info { "Initializing the configuration" }
@@ -185,15 +271,16 @@ class Configuration {
 
         /**
          * Parses a configuration file
-         * @author Florian Wendland
+         *
          * @param configFile the path pointing to the file
          * @return a Configuration object
+         * @author Florian Wendland
          */
         private fun parse(configFile: Path): Configuration {
-            logger.info { "Trying to parse the configuration file" }
+            logger.info { "Trying to parse the configuration file at $configFile" }
             val file = configFile.toFile()
             if (!file.exists() || !file.isFile || !file.canRead()) {
-                logger.warn { "Could not read from the configuration file" }
+                logger.warn { "Could not read from the configuration file at $configFile" }
                 return Configuration()
             }
 
@@ -206,7 +293,7 @@ class Configuration {
             } catch (e: IOException) {
                 // also catches more specific Jackson exceptions
                 logger.error(e) {
-                    "Failed creating a valid configuration from the configuration file: $e"
+                    "Failed creating a valid configuration from the configuration file: ${e.localizedMessage}"
                 }
             }
             return Configuration()
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/MarkResolver.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/MarkResolver.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8909a1f89cf3b75439463fbe06e42e372c5a9174
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/MarkResolver.kt
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.main
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.File
+import java.nio.file.Path
+
+private val logger = KotlinLogging.logger {}
+
+/**
+ * Tries to resolve the location of the builtin MARK files. This assumes the following structure of
+ * the codyze-medina installation:
+ * - bin/
+ *     - codyze-medina
+ * - lib/
+ *     - ...
+ * - mark/
+ *     - ...
+ *
+ * @return The Path of the parent directory containing all builtin MARK rules
+ */
+private fun resolveMarkLocation(): Path {
+    // The following resolves the location of the codyze-medina-{version}.jar within lib/ and from
+    // there navigates to mark/
+    val path =
+        Path.of(CodyzeMedina.Companion::class.java.protectionDomain.codeSource.location.toURI())
+            .resolve("../../mark/")
+            .normalize()
+    logger.debug { "Determined the location of the MARK files as $path" }
+    return path
+}
+
+/**
+ * Converts the MARK location arguments of the configuration to a single list that can be passed to
+ * Codyze
+ *
+ * @param builtin The argument list of builtin MARK files
+ * @param project The argument list of external MARK files
+ * @param base The base path used for relative MARK file locations
+ * @return A combined list of resolved paths to the needed MARK files
+ */
+fun convertMarkConfig(builtin: List<String>, project: List<String>, base: Path): List<String> {
+    val markList = mutableListOf<String>()
+    val markLocation = resolveMarkLocation().toString()
+    for (name in builtin) {
+        val mark = "$markLocation/$name"
+        markList.add(mark)
+    }
+    for (path in project) {
+        val resolvedPath = relativeLocation(File(path), base)
+        markList.add(resolvedPath.canonicalPath)
+    }
+    markList.forEach { logger.debug { "Added MARK file location ${println(it)}" } }
+    return markList
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/Mapping.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/Mapping.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b2072227e383f114d531a27956f333b46c90d04c
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/Mapping.kt
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.mapping
+
+/** Maps Mark rules of Findings to the respective AssessmentResults */
+@Suppress("unused")
+class Mapping {
+    lateinit var metrics: Array<Metric>
+}
+
+@Suppress("unused")
+class Metric {
+    lateinit var name: String // the name of the metric
+    lateinit var rules: Array<String> // the name of the rules which need to be fulfilled
+    lateinit var configuration: Configuration // the configuration for this metric
+}
+
+@Suppress("unused")
+class Configuration {
+    var default: Boolean = true // whether this is a default metric
+    lateinit var operator: String // which operator compares this metric
+    lateinit var type: Type // of which type the target values are
+    lateinit var target: Array<Any> // what values are desired for this metric
+}
+
+@Suppress("unused")
+enum class Type {
+    STRING,
+    BOOLEAN,
+    NUMBER
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MappingTree.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MappingTree.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fb7a2222d9145afdc9b9c7c77131ce7817d44c14
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MappingTree.kt
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.mapping
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.File
+
+private val logger = KotlinLogging.logger {}
+const val RESOURCE_TYPE = "Application"
+const val MAP_FILE_NAME = "mapping.yaml"
+
+/**
+ * This function recursively steps through each branch of the hierarchy tree of the mark rule
+ * directory until it finds a mapping which is applied for all rules specified within this
+ * directory. Multiple mapping files can be specified in different directories within the same
+ * hierarchy layer. Rules in directories with no mappings will be ignored. All mapping files in
+ * lower hierarchy layers will be ignored.
+ *
+ * @param markOrigin the Path specified as the origin of the Mark rules
+ * @return a Map assigning each specified mapping to its directory
+ * @author Robert Haimerl
+ */
+fun parseMappingTree(markOrigin: File): Map<Mapping, File> {
+    // stop if we arrived at single file
+    if (!markOrigin.isDirectory) return mapOf()
+
+    // if this directory contains a valid mapping, stop and add it to the map
+    val mapFile = File(markOrigin, MAP_FILE_NAME)
+    if (File(markOrigin, MAP_FILE_NAME).exists()) {
+        val mapping = parseMapping(mapFile)
+        if (mapping != null) {
+            return mapOf(mapping to markOrigin)
+        } else {
+            logger.warn { "Ignoring invalid mapping file at $mapFile" }
+        }
+    }
+
+    // if there is no valid mapping, recursively search subdirectories for mappings
+    val mappingToDirectory = mutableMapOf<Mapping, File>()
+    for (branch: File in markOrigin.listFiles()!!) {
+        mappingToDirectory += parseMappingTree(branch)
+    }
+    return mappingToDirectory
+}
+
+/**
+ * Parses the given file which contains instructions on how to map Findings to AssessmentResults.
+ *
+ * @param mapFile the File which includes the mapping information
+ * @return the Mapping object derived from the specification or null if parsing failed
+ * @author Robert Haimerl
+ */
+fun parseMapping(mapFile: File): Mapping? {
+    logger.info { "Parsing mappings from ${mapFile.name}" }
+
+    // parse the yaml file into a Mapping object
+    val yamlMapper = ObjectMapper(YAMLFactory())
+    return try {
+        yamlMapper.readValue(mapFile, Mapping::class.java)
+    } catch (e: Exception) {
+        logger.error(e) {
+            "Failed to parse the mapping file located at $mapFile: ${e.localizedMessage}"
+        }
+        null
+    }
+}
+
+/**
+ * Parses the given file which contains instructions on how to construct AssessmentResults from
+ * Medina checks
+ *
+ * @param mapFile the File which includes the mapping information
+ * @return the MedinaMapping object derived from the specification
+ * @author Robert Haimerl
+ */
+fun parseMedinaMapping(mapFile: File): MedinaMapping? {
+    logger.info { "Parsing Medina mappings from ${mapFile.name}" }
+
+    // parse the yaml file into a Mapping object, throw a RuntimeException on Error
+    val yamlMapper = ObjectMapper(YAMLFactory())
+    return try {
+        yamlMapper.readValue(mapFile, MedinaMapping::class.java)
+    } catch (e: Exception) {
+        logger.warn { "Medina Mapping file was not found or cannot be parsed." }
+        null
+    }
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MedinaMapping.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MedinaMapping.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8c624cd1721198f4c89a04cf15be5e9b26858825
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MedinaMapping.kt
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.mapping
+
+/** Similar to the Mapping, but only specifies the name and the target values */
+@Suppress("unused")
+class MedinaMapping {
+    lateinit var metrics: Array<MedinaMetric>
+}
+
+@Suppress("unused")
+class MedinaMetric {
+    lateinit var name: String // the name of the metric
+    lateinit var target: Array<Any> // the targets for this metrics
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Assemblers.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Assemblers.kt
deleted file mode 100644
index 531450789b2a01e96a500c6b2d8cfb713f95a937..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Assemblers.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina.util
-
-import java.io.File
-import java.io.IOException
-import java.util.*
-import mu.KotlinLogging
-import org.openapitools.client.evidence.model.Evidence
-import org.openapitools.client.orchestrator.model.AssessmentTool
-
-private val logger = KotlinLogging.logger {}
-private val codyzeVersion = "1.0.0"
-
-/**
- * Creates a Codyze AssessmentTool
- * @author Robert Haimerl
- * @param metrics a list of all available metrics for this tool
- * @return the resulting AssessmentTool
- */
-fun createAssessmentTool(metrics: List<String>): AssessmentTool {
-    logger.debug { "Creating the AssessmentTool" }
-
-    val at = AssessmentTool()
-    at.id = "codyze-$codyzeVersion"
-    at.name = "MEDINA Codyze v$codyzeVersion"
-    // Description taken from the introduction of the GitHub README
-    at.description =
-        "Codyze is a static code analyzer that focuses on verifying security compliance in source code. It operates on code property graphs and is thus able to handle non-compiling or even incomplete code fragments."
-    // TODO: hardcoded for now, needs to be able to fetch metrics from available rules
-    at.availableMetrics = metrics
-
-    return at
-}
-
-/**
- * Creates an Evidence
- * @author Robert Haimerl
- * @param resultFilePath the path to the results file used as evidence
- * @return the resulting Evidence
- */
-fun createEvidence(resultFilePath: String): Evidence {
-    logger.debug { "Creating new Evidence based on $resultFilePath" }
-    val ev = Evidence()
-
-    ev.id = UUID.randomUUID().toString()
-    ev.timestamp = java.time.OffsetDateTime.now()
-    ev.serviceId = null
-    ev.toolId = "codyze-$codyzeVersion"
-    ev.raw =
-        try {
-            File(resultFilePath).bufferedReader().use { it.readText() }
-        } catch (e: IOException) {
-            ""
-        }
-    ev.resource =
-        object {
-            @Suppress("unused") val id = hash
-        }
-    return ev
-}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Environment.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Environment.kt
deleted file mode 100644
index 990cd3874d4eca191aaf7b4f58f0b3a716b873ec..0000000000000000000000000000000000000000
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Environment.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina.util
-
-import de.fraunhofer.aisec.codyze.medina.Configuration.Environment
-import java.io.BufferedReader
-import java.io.InputStreamReader
-import mu.KotlinLogging
-
-private val logger = KotlinLogging.logger {}
-var environment = Environment.NONE
-var environmentVariables = mutableMapOf<String, String>()
-var hash: String? = null
-
-/**
- * Tries multiple known environment variables to find out which CI/CD environment is being used
- * Currently possible values for environment are ["None", "GitHub", "GitLab", "Jenkins"]. This
- * method sets the "environment"-variable and fills the "environmentVariables"-Map specific to this
- * environment
- * @author Robert Haimerl
- */
-fun verifyEnvironment() {
-    // all known CI/CD environments set "CI" to "true"
-    if (System.getenv("CI") != "true") {
-        logger.warn { "No CI/CD environment recognized" }
-        return
-    }
-
-    val githubVars = arrayOf("GITHUB_ACTION", "GITHUB_JOB", "GITHUB_SHA")
-    val gitlabVars = arrayOf("CI_PIPELINE_ID", "CI_JOB_ID", "CI_COMMIT_SHA")
-    val jenkinsVars = arrayOf("JOB_NAME", "BUILD_ID", "JENKINS_URL")
-
-    if (githubVars.all { variable -> System.getenv(variable) != null })
-        environment = Environment.GITHUB
-    else if (gitlabVars.all { variable -> System.getenv(variable) != null })
-        environment = Environment.GITLAB
-    else if (jenkinsVars.all { variable -> System.getenv(variable) != null })
-        environment = Environment.JENKINS
-
-    if (environment == Environment.NONE) logger.warn { "CI/CD environment could not be determined" }
-    else logger.info { "Determined CI/CD environment to be $environment" }
-}
-
-/**
- * Adds all relevant environment variables for the detected CI environment to the Map
- * @author Robert Haimerl
- */
-fun addEnvironmentVariables() {
-    // jenkins: "SCM-specific variables such as GIT_COMMIT are not automatically defined as
-    // environment variables; rather you can use the return value of the checkout step"
-    when (environment) {
-        Environment.GITHUB -> environmentVariables["commit_hash"] = "GITHUB_SHA"
-        Environment.GITLAB -> environmentVariables["commit_hash"] = "CI_COMMIT_SHA"
-        else -> return
-    }
-}
-
-/**
- * Tries environment variables set by GitHub actions, then GitLab CI/CD before ultimately falling
- * back to console commands. This method modifies the "hash" member.
- * @author Robert Haimerl
- */
-@kotlin.jvm.Throws(RuntimeException::class)
-fun fetchGitHash() {
-    // Default to "GITHUB_SHA" to prevent NullPointerExceptions from being thrown
-    hash = System.getenv(environmentVariables["commit_hash"] ?: "GITHUB_SHA")
-    if (hash == null) {
-        val process = Runtime.getRuntime().exec("git rev-parse HEAD")
-        val reader = BufferedReader(InputStreamReader(process.inputStream))
-        hash = reader.readLine()
-    }
-    // If we couldn't figure hash out, abort
-    if (hash == null)
-        throw RuntimeException("Unable to fetch the Git commit hash for the analyzed project")
-}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/MedinaSarif.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/MedinaSarif.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9997cea0e76a448faaf5674d5d5e835d0d27dd54
--- /dev/null
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/MedinaSarif.kt
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.util
+
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
+import de.fraunhofer.aisec.codyze.medina.main.CodyzeMedina
+import io.github.detekt.sarif4k.*
+import java.nio.file.Path
+import java.util.stream.Collectors
+import kotlin.io.path.deleteIfExists
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+
+// The rules added to the ToolComponent, derived from evaluation.Rule
+// NOTE: the order of rules is important as it is later referenced in the field "ruleIndex"
+val rules: List<ReportingDescriptor> =
+    Rule.entries
+        .stream()
+        .map {
+            ReportingDescriptor(
+                name = it.name,
+                id = it.id,
+                fullDescription = MultiformatMessageString(text = it.description)
+            )
+        }
+        .collect(Collectors.toList())
+
+// The tool with Codyze-Medina as its driver and all custom rules
+val codyzeMedinaComponent: ToolComponent =
+    ToolComponent(
+        name = "CodyzeMedina",
+        fullName = "CodyzeMedina ${CodyzeMedina.CODYZE_VERSION}",
+        organization = "Fraunhofer AISEC",
+        version = CodyzeMedina.CODYZE_VERSION,
+        rules = rules,
+    )
+
+/**
+ * Prints a separate SARIF report from the MEDINA evaluation result.
+ *
+ * @param evaluationResults The MEDINA evaluation results
+ * @param filepath The location of the resulting SARIF file
+ * @author Robert Haimerl
+ */
+fun printSarifReport(evaluationResults: Map<Rule, EvaluationResult>, filepath: Path) {
+    val report = createSarifReport(evaluationResults)
+    filepath.writeText(SarifSerializer.toJson(report))
+}
+
+/**
+ * Takes the last run of an existing SARIF report (e.g. from the Codyze library) and adds it to the
+ * MEDINA report as an extension.
+ *
+ * @param oldLocation The location of the previous SARIF report. MUST be valid SARIF
+ * @param newLocation The location of the new SARIF report after amending.
+ * @param evaluationResults The MEDINA evaluation results
+ * @author Robert Haimerl
+ */
+fun amendExistingReport(
+    oldLocation: Path,
+    newLocation: Path,
+    evaluationResults: Map<Rule, EvaluationResult>
+) {
+    val originalReport = oldLocation.readText()
+    oldLocation.deleteIfExists()
+    val originalSarif = SarifSerializer.fromJson(originalReport)
+    val lastRun = originalSarif.runs.last()
+
+    // Add Codyze-Medina as the new driver and move the original driver to extensions
+    val newTool =
+        lastRun.tool.copy(
+            driver = codyzeMedinaComponent,
+            extensions = (lastRun.tool.extensions?.plus(listOf(lastRun.tool.driver)))
+        )
+    val newResults = lastRun.results?.plus(collectResults(evaluationResults))
+
+    // Create the new runs by using the changed last run
+    val newRun = lastRun.copy(tool = newTool, results = newResults)
+    val newRuns: MutableList<Run> =
+        originalSarif.runs.subList(0, originalSarif.runs.size - 1).toMutableList()
+    newRuns.add(newRun)
+
+    // Create the new SARIF by using the new runs
+    val newSarif = originalSarif.copy(runs = newRuns)
+    val newReport = SarifSerializer.toJson(newSarif)
+    newLocation.writeText(newReport)
+}
+
+/**
+ * This function creates a valid SARIF report from the MEDINA rule evaluations It is a separate
+ * report with CodyzeMedina as a separate tool driver and not an extension
+ *
+ * @param evaluationResults The results of evaluation the MEDINA rules
+ * @author Robert Haimerl
+ */
+private fun createSarifReport(evaluationResults: Map<Rule, EvaluationResult>): SarifSchema210 {
+    val results = collectResults(evaluationResults)
+    return SarifSchema210(
+        schema =
+            "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+        version = Version.The210,
+        runs =
+            listOf(
+                Run(
+                    tool = Tool(driver = codyzeMedinaComponent),
+                    results = results,
+                )
+            )
+    )
+}
+
+/**
+ * Converts the evaluation results to SARIF results
+ *
+ * @param evaluationResults The MEDINA evaluation results
+ * @return the corresponding SARIF results
+ * @author Robert Haimerl
+ */
+private fun collectResults(evaluationResults: Map<Rule, EvaluationResult>): List<Result> {
+    return evaluationResults.entries
+        .stream()
+        .map { (rule, evResult) ->
+            Result(
+                ruleID = rule.id,
+                kind = if (evResult.isValidated()) ResultKind.Pass else ResultKind.Fail,
+                message = Message(text = rule.description),
+            )
+        }
+        .collect(Collectors.toList())
+}
diff --git a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Util.kt b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Util.kt
index d9368df79120f7357dc5ab54a5eaef99cce15c7e..f433609e28f7c65ed2dcd9cdfce5f26fc84ab064 100644
--- a/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Util.kt
+++ b/src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Util.kt
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 /* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,23 +30,22 @@ package de.fraunhofer.aisec.codyze.medina.util
 
 import de.fraunhofer.aisec.codyze.analysis.Finding
 import de.fraunhofer.aisec.codyze.config.Configuration
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
 import de.fraunhofer.aisec.codyze.printer.LegacyPrinter
 import de.fraunhofer.aisec.codyze.printer.Printer
 import de.fraunhofer.aisec.codyze.printer.SarifPrinter
-import java.io.InputStreamReader
-import java.util.*
-import mu.KotlinLogging
-import org.openapitools.client.orchestrator.model.AssessmentResult
-import org.openapitools.client.orchestrator.model.MetricConfiguration
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.nio.file.Path
 
 private val logger = KotlinLogging.logger {}
-private val mapping = mutableMapOf<String, Pair<String, MetricConfiguration>>()
 
 /**
  * Prints the Findings the way specified in the configuration
- * @author Robert Haimerl
+ *
  * @param findings the set of Findings
- * @param config the config containing printing details (path and SARIF)
+ * @param config the configuration of codyze-medina
+ * @author Robert Haimerl
  */
 fun printFindings(findings: Set<Finding>, config: Configuration) {
     val printer: Printer =
@@ -59,80 +58,45 @@ fun printFindings(findings: Set<Finding>, config: Configuration) {
 }
 
 /**
- * Creates an AssessmentResult from a Finding
- * @author Robert Haimerl
- * @param finding the Finding to be transformed
- * @param evidenceId the id of the Evidence belonging to the AssessmentResult
- * @return the resulting AssessmentResult or null if not included in mapping.txt
- */
-fun findingToAR(finding: Finding, evidenceId: String): AssessmentResult? {
-    logger.debug { "Creating AssessmentResult for finding with id ${finding.identifier}" }
-    val ar = AssessmentResult()
-
-    // TODO: currently ignores EVERY finding not specified in mappings.txt
-    if (mapping[finding.identifier] == null) {
-        logger.debug { "Finding with id ${finding.identifier} not specified; Ignoring" }
-        return null
-    }
-
-    val mc = mapping[finding.identifier]!!.second
-
-    ar.metricConfiguration = mc
-    ar.id = UUID.randomUUID().toString()
-    ar.timestamp = java.time.OffsetDateTime.now()
-    ar.evidenceId = evidenceId
-    ar.resourceId = hash
-    ar.metricId = mapping[finding.identifier]!!.first
-    ar.compliant = !finding.isProblem
-    ar.nonComplianceComments = finding.logMsg
-
-    return ar
-}
-
-/**
- * Parses the given file which contains instructions on how to map Findings to AssessmentResults.
- * The Resulting mappings are being added to "mapping"
+ * Creates a concluding analysis report, intended at to be at the very end
+ *
+ * @param medinaEvaluation The results from evaluating the Medina Metrics
+ * @param markViolationNumber The number of violations of MARK rules as returned by Codyze
+ * @param codyzeOutput The location of the output from the Codyze library
+ * @param output The location of the output as specified in the configuration
+ * @param combinedOutput Whether both output files were combined
  * @author Robert Haimerl
- * @param mapFilePath the path of the file which includes the mapping information
  */
-fun parseMapping(mapFilePath: String) {
-    logger.info { "Parsing mappings from $mapFilePath" }
+fun createLogReport(
+    medinaEvaluation: Map<Rule, EvaluationResult>,
+    markViolationNumber: Int,
+    codyzeOutput: String,
+    output: Path,
+    combinedOutput: Boolean
+) {
+    logger.debug { "Creating the final report" }
+    // Filter for violated Rules
+    val negativeEvaluation = medinaEvaluation.filter { eval -> !eval.value.isValidated() }
 
-    val mapfile = InputStreamReader(ClassLoader.getSystemResourceAsStream(mapFilePath) ?: return)
-    // read from mappings.txt
-    val rawMappings = mapfile.useLines { it.toList() }
-    // parse each mapping and add it to a map
-    for (raw in rawMappings) {
-        val ar = raw.split("->")[1].drop(1).dropLast(1).split(";")
-        val kind = ar[4]
-        val targetValue: Any =
-        // in case we have a list of target values
-        if (ar[3].contains(":"))
-                when (kind) {
-                    "N" -> ar[3].split(":").map { v: String -> v.toDouble() }
-                    "B" -> ar[3].split(":").map { v: String -> v == "true" }
-                    else -> ar[3].split(":")
-                }
-            // single values
-            else
-                when (kind) {
-                    "N" -> ar[3].toDouble()
-                    "B" -> ar[3] == "true"
-                    else -> ar[3]
-                }
+    // Create the header of the summary
+    val summaryHeader = "\n-----------\n" + "Summary of Analysis\n" + "-----------\n\n"
+    // Create the summary of the MARK evaluation
+    val markSummary =
+        "Found $markViolationNumber violations of MARK rules.\n" +
+            "The Analysis has been written to ${if (combinedOutput) output else codyzeOutput}\n\n"
 
-        // create a new MetricConfiguration according to the file
-        val mc = MetricConfiguration()
-        mc.isDefault = ar[1] == "true"
-        mc.operator = ar[2]
-        mc.targetValue = targetValue
-
-        // use the metric name and configuration as values of the map
-        val mapValue = Pair(ar[0], mc)
+    // Create the summary of the MEDINA evaluation
+    var medinaSummary =
+        "Found ${if (negativeEvaluation.isNotEmpty()) negativeEvaluation.size else "no"} violations of MEDINA rules" +
+            if (negativeEvaluation.isNotEmpty()) ":\n" else "\n"
+    for (eval in negativeEvaluation) {
+        medinaSummary += "\t- Rule ${eval.key}\n" + "\t\t \"${eval.value.getMessage()}\"\n"
+    }
 
-        // the findings we want to map to this assessmentResult
-        val findings = raw.split("->")[0].drop(1).dropLast(1).split(";")
-        for (mapKey in findings) if (mapKey != "") mapping[mapKey] = mapValue
+    // Print the whole message into the log
+    if (negativeEvaluation.isEmpty() && markViolationNumber == 0) {
+        logger.info { summaryHeader + markSummary + medinaSummary }
+    } else {
+        logger.error { summaryHeader + markSummary + medinaSummary }
     }
-    logger.debug { "Mappings: $mapping" }
 }
diff --git a/src/main/resources/evidence.yaml b/src/main/resources/evidence.yaml
index 9edfed182167feacd3a880509c8d4f83ef477bf5..4497d59ca1049acfdbb6a2461ce5a680f5a8c401 100644
--- a/src/main/resources/evidence.yaml
+++ b/src/main/resources/evidence.yaml
@@ -41,8 +41,17 @@ paths:
             description: Returns all stored evidences. Part of the public API, also exposed as REST.
             operationId: EvidenceStore_ListEvidences
             parameters:
+                - name: filter.cloudServiceId
+                  in: query
+                  schema:
+                    type: string
+                - name: filter.toolId
+                  in: query
+                  schema:
+                    type: string
                 - name: pageSize
                   in: query
+                  description: 'page_size: 0 = default (50 is default value), > 0 = set value (i.e. page_size = 5 -> SQL-Limit = 5)'
                   schema:
                     type: integer
                     format: int32
@@ -71,6 +80,33 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
+    /v1/evidence_store/evidences/{evidenceId}:
+        get:
+            tags:
+                - EvidenceStore
+            description: |-
+                Returns a particular stored evidence. Part of the public API, also exposed
+                 as REST.
+            operationId: EvidenceStore_GetEvidence
+            parameters:
+                - name: evidenceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Evidence'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
 components:
     schemas:
         Evidence:
@@ -83,7 +119,7 @@ components:
                     type: string
                     description: time of evidence creation
                     format: date-time
-                serviceId:
+                cloudServiceId:
                     type: string
                     description: Reference to a service this evidence was gathered from
                 toolId:
@@ -91,7 +127,7 @@ components:
                     description: Reference to the tool which provided the evidence
                 raw:
                     type: string
-                    description: Contains the evidence in its original form without following a defined schema, e.g. the raw JSON
+                    description: Optional. Contains the evidence in its original form without following a defined schema, e.g. the raw JSON
                 resource:
                     $ref: '#/components/schemas/GoogleProtobufValue'
             description: An evidence resource
@@ -132,10 +168,7 @@ components:
             description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).'
         StoreEvidenceResponse:
             type: object
-            properties:
-                status:
-                    type: boolean
-                statusMessage:
-                    type: string
+            properties: {}
+            description: StoreEvidenceResponse belongs to StoreEvidence, which uses a custom unary RPC and therefore requires a response message according to the style convention. Since no return values are required, this is empty.
 tags:
     - name: EvidenceStore
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000000000000000000000000000000..bda1b36172385daa71df036b972b76318128dc7d
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,9 @@
+# Set root logger level to WARN and its only appender to A1.
+log4j.rootLogger=WARN, A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index 1d1b7e18b0a7718f6d0ab905e1d1699790574f0e..23bbbda8e782ca4be279a2cd3a073c9fa238065e 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -5,7 +5,7 @@
             <patternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
         </console>
 
-        <file name="fileout" fileName="medina-codyze.log">
+        <file name="fileout" fileName="codyze-medina.log">
             <patternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L - %m%n"/>
         </file>
     </appenders>
diff --git a/src/main/resources/orchestrator.yaml b/src/main/resources/orchestrator.yaml
index bc269e13bfce2682882ba0ef3e588403bf93dc56..e3a12006df802697fc7fbb1fa2d950fdcd445740 100644
--- a/src/main/resources/orchestrator.yaml
+++ b/src/main/resources/orchestrator.yaml
@@ -14,6 +14,37 @@ paths:
             description: List all assessment results. Part of the public API, also exposed as REST.
             operationId: Orchestrator_ListAssessmentResults
             parameters:
+                - name: filter.cloudServiceId
+                  in: query
+                  description: Optional. List only assessment results of a specific cloud service.
+                  schema:
+                    type: string
+                - name: filter.compliant
+                  in: query
+                  description: Optional. List only compliant assessment results.
+                  schema:
+                    type: boolean
+                - name: filter.metricIds
+                  in: query
+                  description: Optional. List only assessment results of a specific metric id.
+                  schema:
+                    type: array
+                    items:
+                        type: string
+                - name: filter.metricId
+                  in: query
+                  schema:
+                    type: string
+                - name: filter.toolId
+                  in: query
+                  description: Optional. List only assessment result from a specific assessment tool.
+                  schema:
+                    type: string
+                - name: latestByResourceId
+                  in: query
+                  description: Optional. Latest results grouped by resource_id and metric_id.
+                  schema:
+                    type: boolean
                 - name: pageSize
                   in: query
                   schema:
@@ -68,6 +99,31 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
+    /v1/orchestrator/assessment_results/{id}:
+        get:
+            tags:
+                - Orchestrator
+            description: Get an assessment result by ID
+            operationId: Orchestrator_GetAssessmentResult
+            parameters:
+                - name: id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/AssessmentResult'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
     /v1/orchestrator/assessment_tools:
         get:
             tags:
@@ -77,9 +133,30 @@ paths:
                  passed metric id
             operationId: Orchestrator_ListAssessmentTools
             parameters:
-                - name: metricId
+                - name: filter.cloudServiceId
+                  in: query
+                  description: Optional. List only assessment results of a specific cloud service.
+                  schema:
+                    type: string
+                - name: filter.compliant
+                  in: query
+                  description: Optional. List only compliant assessment results.
+                  schema:
+                    type: boolean
+                - name: filter.metricIds
+                  in: query
+                  description: Optional. List only assessment results of a specific metric id.
+                  schema:
+                    type: array
+                    items:
+                        type: string
+                - name: filter.metricId
                   in: query
-                  description: filter tools by metric id
+                  schema:
+                    type: string
+                - name: filter.toolId
+                  in: query
+                  description: Optional. List only assessment result from a specific assessment tool.
                   schema:
                     type: string
                 - name: pageSize
@@ -136,18 +213,24 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/assessment_tools/{toolId}:
-        get:
+    /v1/orchestrator/assessment_tools/{tool.id}:
+        put:
             tags:
                 - Orchestrator
-            description: Returns assessment tool given by the passed tool id
-            operationId: Orchestrator_GetAssessmentTool
+            description: Updates the assessment tool given by the passed id
+            operationId: Orchestrator_UpdateAssessmentTool
             parameters:
-                - name: toolId
+                - name: tool.id
                   in: path
                   required: true
                   schema:
                     type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/AssessmentTool'
+                required: true
             responses:
                 "200":
                     description: OK
@@ -161,23 +244,18 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-        put:
+    /v1/orchestrator/assessment_tools/{toolId}:
+        get:
             tags:
                 - Orchestrator
-            description: Updates the assessment tool given by the passed id
-            operationId: Orchestrator_UpdateAssessmentTool
+            description: Returns assessment tool given by the passed tool id
+            operationId: Orchestrator_GetAssessmentTool
             parameters:
                 - name: toolId
                   in: path
                   required: true
                   schema:
                     type: string
-            requestBody:
-                content:
-                    application/json:
-                        schema:
-                            $ref: '#/components/schemas/AssessmentTool'
-                required: true
             responses:
                 "200":
                     description: OK
@@ -214,12 +292,14 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/certificates:
+    /v1/orchestrator/catalogs:
         get:
             tags:
                 - Orchestrator
-            description: Lists all target certificates
-            operationId: Orchestrator_ListCertificates
+            description: |-
+                Lists all security controls catalogs. Each catalog includes a list of its
+                 categories but no additional sub-resources.
+            operationId: Orchestrator_ListCatalogs
             parameters:
                 - name: pageSize
                   in: query
@@ -244,7 +324,7 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/ListCertificatesResponse'
+                                $ref: '#/components/schemas/ListCatalogsResponse'
                 default:
                     description: Default error response
                     content:
@@ -254,13 +334,13 @@ paths:
         post:
             tags:
                 - Orchestrator
-            description: Creates a new certificate
-            operationId: Orchestrator_CreateCertificate
+            description: Creates a new security controls catalog
+            operationId: Orchestrator_CreateCatalog
             requestBody:
                 content:
                     application/json:
                         schema:
-                            $ref: '#/components/schemas/Certificate'
+                            $ref: '#/components/schemas/Catalog'
                 required: true
             responses:
                 "200":
@@ -268,62 +348,65 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/Certificate'
+                                $ref: '#/components/schemas/Catalog'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/certificates/{certificateId}:
-        get:
+    /v1/orchestrator/catalogs/{catalog.id}:
+        put:
             tags:
                 - Orchestrator
-            description: Retrieves a certificate
-            operationId: Orchestrator_GetCertificate
+            description: Updates an existing certificate
+            operationId: Orchestrator_UpdateCatalog
             parameters:
-                - name: certificateId
+                - name: catalog.id
                   in: path
                   required: true
                   schema:
                     type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/Catalog'
+                required: true
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/Certificate'
+                                $ref: '#/components/schemas/Catalog'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-        put:
+    /v1/orchestrator/catalogs/{catalogId}:
+        get:
             tags:
                 - Orchestrator
-            description: Updates an existing certificate
-            operationId: Orchestrator_UpdateCertificate
+            description: |-
+                Retrieves a specific catalog by it's ID. The catalog includes a list of all
+                 of it categories as well as the first level of controls in each category.
+            operationId: Orchestrator_GetCatalog
             parameters:
-                - name: certificateId
+                - name: catalogId
                   in: path
                   required: true
                   schema:
                     type: string
-            requestBody:
-                content:
-                    application/json:
-                        schema:
-                            $ref: '#/components/schemas/Certificate'
-                required: true
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/Certificate'
+                                $ref: '#/components/schemas/Catalog'
                 default:
                     description: Default error response
                     content:
@@ -333,10 +416,10 @@ paths:
         delete:
             tags:
                 - Orchestrator
-            description: Removes a certificate
-            operationId: Orchestrator_RemoveCertificate
+            description: Removes a catalog
+            operationId: Orchestrator_RemoveCatalog
             parameters:
-                - name: certificateId
+                - name: catalogId
                   in: path
                   required: true
                   schema:
@@ -351,12 +434,84 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/cloud_services:
+    /v1/orchestrator/catalogs/{catalogId}/categories/{categoryName}/controls/{controlId}:
         get:
             tags:
                 - Orchestrator
-            description: Lists all target cloud services
-            operationId: Orchestrator_ListCloudServices
+            description: |-
+                Retrieves a control specified by the catalog ID, the control's category
+                 name and the control ID. If present, it also includes a list of
+                 sub-controls if present or a list of metrics if no sub-controls but metrics
+                 are present.
+            operationId: Orchestrator_GetControl
+            parameters:
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: categoryName
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: controlId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Control'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/catalogs/{catalogId}/category/{categoryName}:
+        get:
+            tags:
+                - Orchestrator
+            description: |-
+                Retrieves a category of a catalog specified by the catalog ID and the
+                 category name. It includes the first level of controls within each
+                 category.
+            operationId: Orchestrator_GetCategory
+            parameters:
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: categoryName
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Category'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/certificates:
+        get:
+            tags:
+                - Orchestrator
+            description: Lists all target certificates
+            operationId: Orchestrator_ListCertificates
             parameters:
                 - name: pageSize
                   in: query
@@ -381,7 +536,7 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/ListCloudServicesResponse'
+                                $ref: '#/components/schemas/ListCertificatesResponse'
                 default:
                     description: Default error response
                     content:
@@ -391,13 +546,13 @@ paths:
         post:
             tags:
                 - Orchestrator
-            description: Registers a new target cloud service
-            operationId: Orchestrator_RegisterCloudService
+            description: Creates a new certificate
+            operationId: Orchestrator_CreateCertificate
             requestBody:
                 content:
                     application/json:
                         schema:
-                            $ref: '#/components/schemas/CloudService'
+                            $ref: '#/components/schemas/Certificate'
                 required: true
             responses:
                 "200":
@@ -405,62 +560,63 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/CloudService'
+                                $ref: '#/components/schemas/Certificate'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/cloud_services/{serviceId}:
-        get:
+    /v1/orchestrator/certificates/{certificate.id}:
+        put:
             tags:
                 - Orchestrator
-            description: Retrieves a target cloud service
-            operationId: Orchestrator_GetCloudService
+            description: Updates an existing certificate
+            operationId: Orchestrator_UpdateCertificate
             parameters:
-                - name: serviceId
+                - name: certificate.id
                   in: path
                   required: true
                   schema:
                     type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/Certificate'
+                required: true
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/CloudService'
+                                $ref: '#/components/schemas/Certificate'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-        put:
+    /v1/orchestrator/certificates/{certificateId}:
+        get:
             tags:
                 - Orchestrator
-            description: Registers a new target cloud service
-            operationId: Orchestrator_UpdateCloudService
+            description: Retrieves a certificate
+            operationId: Orchestrator_GetCertificate
             parameters:
-                - name: serviceId
+                - name: certificateId
                   in: path
                   required: true
                   schema:
                     type: string
-            requestBody:
-                content:
-                    application/json:
-                        schema:
-                            $ref: '#/components/schemas/CloudService'
-                required: true
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/CloudService'
+                                $ref: '#/components/schemas/Certificate'
                 default:
                     description: Default error response
                     content:
@@ -470,10 +626,10 @@ paths:
         delete:
             tags:
                 - Orchestrator
-            description: Removes a target cloud service
-            operationId: Orchestrator_RemoveCloudService
+            description: Removes a certificate
+            operationId: Orchestrator_RemoveCertificate
             parameters:
-                - name: serviceId
+                - name: certificateId
                   in: path
                   required: true
                   schema:
@@ -488,43 +644,174 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/cloud_services/{serviceId}/metric_configurations:
+    /v1/orchestrator/cloud_services:
         get:
             tags:
                 - Orchestrator
-            description: |-
-                Lists all a metric configurations (target value and operator) for a
-                 specific service ID
-            operationId: Orchestrator_ListMetricConfigurations
+            description: Lists all target cloud services
+            operationId: Orchestrator_ListCloudServices
             parameters:
-                - name: serviceId
-                  in: path
-                  required: true
+                - name: pageSize
+                  in: query
+                  schema:
+                    type: integer
+                    format: int32
+                - name: pageToken
+                  in: query
+                  schema:
+                    type: string
+                - name: orderBy
+                  in: query
                   schema:
                     type: string
+                - name: asc
+                  in: query
+                  schema:
+                    type: boolean
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/ListMetricConfigurationResponse'
+                                $ref: '#/components/schemas/ListCloudServicesResponse'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/cloud_services/{serviceId}/metric_configurations/{metricId}:
-        get:
+        post:
             tags:
                 - Orchestrator
-            description: |-
-                Retrieves a metric configuration (target value and operator) for a specific
-                 service and metric ID
+            description: Registers a new target cloud service
+            operationId: Orchestrator_RegisterCloudService
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/CloudService'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/CloudService'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/statistics:
+        get:
+            tags:
+                - Orchestrator
+            description: Retrieves target cloud service statistics
+            operationId: Orchestrator_GetCloudServiceStatistics
+            parameters:
+                - name: cloudServiceId
+                  in: query
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/GetCloudServiceStatisticsResponse'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloudServiceId}:
+        get:
+            tags:
+                - Orchestrator
+            description: Retrieves a target cloud service
+            operationId: Orchestrator_GetCloudService
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/CloudService'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+        delete:
+            tags:
+                - Orchestrator
+            description: Removes a target cloud service
+            operationId: Orchestrator_RemoveCloudService
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content: {}
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloudServiceId}/metric_configurations:
+        get:
+            tags:
+                - Orchestrator
+            description: |-
+                Lists all a metric configurations (target value and operator) for a
+                 specific service ID
+            operationId: Orchestrator_ListMetricConfigurations
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ListMetricConfigurationResponse'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloudServiceId}/metric_configurations/{metricId}:
+        get:
+            tags:
+                - Orchestrator
+            description: |-
+                Retrieves a metric configuration (target value and operator) for a specific
+                 service and metric ID.
             operationId: Orchestrator_GetMetricConfiguration
             parameters:
-                - name: serviceId
+                - name: cloudServiceId
                   in: path
                   required: true
                   schema:
@@ -555,7 +842,7 @@ paths:
                  service and metric ID
             operationId: Orchestrator_UpdateMetricConfiguration
             parameters:
-                - name: serviceId
+                - name: cloudServiceId
                   in: path
                   required: true
                   schema:
@@ -577,7 +864,345 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/MetricConfiguration'
+                                $ref: '#/components/schemas/MetricConfiguration'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloudServiceId}/toes/{catalogId}:
+        get:
+            tags:
+                - Orchestrator
+            description: Retrieves a Target of Evaluation
+            operationId: Orchestrator_GetTargetOfEvaluation
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/TargetOfEvaluation'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+        delete:
+            tags:
+                - Orchestrator
+            description: Removes a Target of Evaluation
+            operationId: Orchestrator_RemoveTargetOfEvaluation
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content: {}
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloudServiceId}/toes/{catalogId}/controls_in_scope:
+        get:
+            tags:
+                - Orchestrator
+            description: Lists all controls in scope of a target of evaluation.
+            operationId: Orchestrator_ListControlsInScope
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: pageSize
+                  in: query
+                  schema:
+                    type: integer
+                    format: int32
+                - name: pageToken
+                  in: query
+                  schema:
+                    type: string
+                - name: orderBy
+                  in: query
+                  schema:
+                    type: string
+                - name: asc
+                  in: query
+                  schema:
+                    type: boolean
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ListControlsInScopeResponse'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    ? /v1/orchestrator/cloud_services/{cloudServiceId}/toes/{catalogId}/controls_in_scope/categories/{controlCategoryName}/controls/{controlId}
+    :   delete:
+            tags:
+                - Orchestrator
+            description: Adds the selected control as "in scope" for the target of evaluation.
+            operationId: Orchestrator_RemoveControlFromScope
+            parameters:
+                - name: cloudServiceId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: catalogId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: controlCategoryName
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: controlId
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            responses:
+                "200":
+                    description: OK
+                    content: {}
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{cloud_service.id}:
+        put:
+            tags:
+                - Orchestrator
+            description: Registers a new target cloud service
+            operationId: Orchestrator_UpdateCloudService
+            parameters:
+                - name: cloud_service.id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/CloudService'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/CloudService'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    ? /v1/orchestrator/cloud_services/{scope.target_of_evaluation_cloud_service_id}/toes/{scope.target_of_evaluation_catalog_id}/controls_in_scope
+    :   post:
+            tags:
+                - Orchestrator
+            description: Adds the selected control as "in scope" for the target of evaluation.
+            operationId: Orchestrator_AddControlToScope
+            parameters:
+                - name: scope.target_of_evaluation_cloud_service_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: scope.target_of_evaluation_catalog_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/ControlInScope'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ControlInScope'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    ? /v1/orchestrator/cloud_services/{scope.target_of_evaluation_cloud_service_id}/toes/{scope.target_of_evaluation_catalog_id}/controls_in_scope/categories/{scope.control_category_name}/controls/{scope.control_id}
+    :   put:
+            tags:
+                - Orchestrator
+            description: Updates a particular control in scope, e.g., its monitoring status.
+            operationId: Orchestrator_UpdateControlInScope
+            parameters:
+                - name: scope.target_of_evaluation_cloud_service_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: scope.target_of_evaluation_catalog_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: scope.control_category_name
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: scope.control_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/ControlInScope'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ControlInScope'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/cloud_services/{target_of_evaluation.cloud_service_id}/toes/{target_of_evaluation.catalog_id}:
+        put:
+            tags:
+                - Orchestrator
+            description: Updates an existing Target of Evaluation
+            operationId: Orchestrator_UpdateTargetOfEvaluation
+            parameters:
+                - name: target_of_evaluation.cloud_service_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+                - name: target_of_evaluation.catalog_id
+                  in: path
+                  required: true
+                  schema:
+                    type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/TargetOfEvaluation'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/TargetOfEvaluation'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/controls:
+        get:
+            tags:
+                - Orchestrator
+            description: |-
+                If no additional parameters are specified, this lists all controls. If a
+                 catalog ID and a category name is specified, then only controls containing
+                 in this category are returned.
+            operationId: Orchestrator_ListControls
+            parameters:
+                - name: catalogId
+                  in: query
+                  description: return either all controls or only the controls of the specified category
+                  schema:
+                    type: string
+                - name: categoryName
+                  in: query
+                  schema:
+                    type: string
+                - name: pageSize
+                  in: query
+                  schema:
+                    type: integer
+                    format: int32
+                - name: pageToken
+                  in: query
+                  schema:
+                    type: string
+                - name: orderBy
+                  in: query
+                  schema:
+                    type: string
+                - name: asc
+                  in: query
+                  schema:
+                    type: boolean
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ListControlsResponse'
                 default:
                     description: Default error response
                     content:
@@ -645,38 +1270,45 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/metrics/{metricId}:
-        get:
+    /v1/orchestrator/metrics/{implementation.metric_id}/implementation:
+        put:
             tags:
                 - Orchestrator
-            description: Returns the metric with the passed metric id
-            operationId: Orchestrator_GetMetric
+            description: Updates an existing metric implementation
+            operationId: Orchestrator_UpdateMetricImplementation
             parameters:
-                - name: metricId
+                - name: implementation.metric_id
                   in: path
                   required: true
                   schema:
                     type: string
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/MetricImplementation'
+                required: true
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/Metric'
+                                $ref: '#/components/schemas/MetricImplementation'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
+    /v1/orchestrator/metrics/{metric.id}:
         put:
             tags:
                 - Orchestrator
             description: Updates an existing metric
             operationId: Orchestrator_UpdateMetric
             parameters:
-                - name: metricId
+                - name: metric.id
                   in: path
                   required: true
                   schema:
@@ -700,52 +1332,43 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/metrics/{metricId}/implementation:
+    /v1/orchestrator/metrics/{metricId}:
         get:
             tags:
                 - Orchestrator
-            description: Returns the metric implementation of the passed metric id
-            operationId: Orchestrator_GetMetricImplementation
+            description: Returns the metric with the passed metric id
+            operationId: Orchestrator_GetMetric
             parameters:
                 - name: metricId
                   in: path
                   required: true
                   schema:
                     type: string
-                - name: lang
-                  in: query
-                  schema:
-                    type: string
             responses:
                 "200":
                     description: OK
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/MetricImplementation'
+                                $ref: '#/components/schemas/Metric'
                 default:
                     description: Default error response
                     content:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-        put:
+    /v1/orchestrator/metrics/{metricId}/implementation:
+        get:
             tags:
                 - Orchestrator
-            description: Updates an existing metric implementation
-            operationId: Orchestrator_UpdateMetricImplementation
+            description: Returns the metric implementation of the passed metric id
+            operationId: Orchestrator_GetMetricImplementation
             parameters:
                 - name: metricId
                   in: path
                   required: true
                   schema:
                     type: string
-            requestBody:
-                content:
-                    application/json:
-                        schema:
-                            $ref: '#/components/schemas/MetricImplementation'
-                required: true
             responses:
                 "200":
                     description: OK
@@ -759,12 +1382,79 @@ paths:
                         application/json:
                             schema:
                                 $ref: '#/components/schemas/Status'
-    /v1/orchestrator/requirements:
+    /v1/orchestrator/public/certificates:
+        get:
+            tags:
+                - Orchestrator
+            description: Lists all target certificates without state history
+            operationId: Orchestrator_ListPublicCertificates
+            parameters:
+                - name: pageSize
+                  in: query
+                  schema:
+                    type: integer
+                    format: int32
+                - name: pageToken
+                  in: query
+                  schema:
+                    type: string
+                - name: orderBy
+                  in: query
+                  schema:
+                    type: string
+                - name: asc
+                  in: query
+                  schema:
+                    type: boolean
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/ListPublicCertificatesResponse'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/runtime_info:
         get:
             tags:
                 - Orchestrator
-            operationId: Orchestrator_ListRequirements
+            description: Get Runtime Information
+            operationId: Orchestrator_GetRuntimeInfo
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Runtime'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+    /v1/orchestrator/toes:
+        get:
+            tags:
+                - Orchestrator
+            description: Lists all Targets of Evaluation
+            operationId: Orchestrator_ListTargetsOfEvaluation
             parameters:
+                - name: cloudServiceId
+                  in: query
+                  description: We cannot create additional bindings when the parameter is optional so we check for != "" in the method to see if it is set when the service is specified, return all Targets of Evaluation that evaluate the given service for any catalog
+                  schema:
+                    type: string
+                - name: catalogId
+                  in: query
+                  description: when the catalog is specified, return all Targets of Evaluation that evaluate the given catalog for any service
+                  schema:
+                    type: string
                 - name: pageSize
                   in: query
                   schema:
@@ -788,7 +1478,31 @@ paths:
                     content:
                         application/json:
                             schema:
-                                $ref: '#/components/schemas/ListRequirementsResponse'
+                                $ref: '#/components/schemas/ListTargetsOfEvaluationResponse'
+                default:
+                    description: Default error response
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/Status'
+        post:
+            tags:
+                - Orchestrator
+            description: Creates a new Target of Evaluation
+            operationId: Orchestrator_CreateTargetOfEvaluation
+            requestBody:
+                content:
+                    application/json:
+                        schema:
+                            $ref: '#/components/schemas/TargetOfEvaluation'
+                required: true
+            responses:
+                "200":
+                    description: OK
+                    content:
+                        application/json:
+                            schema:
+                                $ref: '#/components/schemas/TargetOfEvaluation'
                 default:
                     description: Default error response
                     content:
@@ -837,6 +1551,12 @@ components:
                 nonComplianceComments:
                     type: string
                     description: Some comments on the reason for non-compliance
+                cloudServiceId:
+                    type: string
+                    description: The cloud service which this assessment result belongs to
+                toolId:
+                    type: string
+                    description: Reference to the tool which provided the assessment result
             description: A result resource, representing the result after assessing the cloud resource with id resource_id.
         AssessmentTool:
             type: object
@@ -853,6 +1573,52 @@ components:
                         type: string
                     description: a list of metrics that this tool can assess, referred by their ids
             description: Represents an external tool or service that offers assessments according to certain metrics.
+        Catalog:
+            type: object
+            properties:
+                id:
+                    type: string
+                name:
+                    type: string
+                description:
+                    type: string
+                categories:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Category'
+                allInScope:
+                    type: boolean
+                    description: Certain security catalogs do not allow to select the scope of the controls, but all controls are automatically "in scope", however they can be set to a DELEGATED status.
+                assuranceLevels:
+                    type: array
+                    items:
+                        type: string
+                    description: A list of the assurance levels, e.g., basic, substantial and high for the EUCS catalog.
+                shortName:
+                    type: string
+                    description: Catalogs short name, e.g. EUCS
+                metadata:
+                    $ref: '#/components/schemas/Catalog_Metadata'
+        Catalog_Metadata:
+            type: object
+            properties:
+                color:
+                    type: string
+                    description: a color for the cloud service used by the UI
+        Category:
+            type: object
+            properties:
+                name:
+                    type: string
+                catalogId:
+                    type: string
+                    description: Reference to the catalog this category belongs to.
+                description:
+                    type: string
+                controls:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Control'
         Certificate:
             type: object
             properties:
@@ -860,7 +1626,7 @@ components:
                     type: string
                 name:
                     type: string
-                serviceId:
+                cloudServiceId:
                     type: string
                 issueDate:
                     type: string
@@ -889,15 +1655,120 @@ components:
                     type: string
                 description:
                     type: string
-                requirements:
-                    $ref: '#/components/schemas/CloudService_Requirements'
-        CloudService_Requirements:
+                catalogsInScope:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Catalog'
+                configuredMetrics:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Metric'
+                createdAt:
+                    type: string
+                    description: creation time of the cloud_service
+                    format: date-time
+                updatedAt:
+                    type: string
+                    description: last update time of the cloud_service
+                    format: date-time
+                metadata:
+                    $ref: '#/components/schemas/CloudService_Metadata'
+        CloudService_Metadata:
+            type: object
+            properties:
+                labels:
+                    type: object
+                    additionalProperties:
+                        type: string
+                    description: a map of key/value pairs, e.g., env:prod
+                icon:
+                    type: string
+                    description: an icon for the cloud service used by the UI
+        Control:
             type: object
             properties:
-                requirementIds:
+                id:
+                    type: string
+                    description: A short name of the control, e.g. OPS-01, as used in OSCAL; it is not a unique ID!
+                categoryName:
+                    type: string
+                categoryCatalogId:
+                    type: string
+                name:
+                    type: string
+                    description: Human-readable name of the control
+                description:
+                    type: string
+                    description: Description of the control
+                controls:
                     type: array
                     items:
-                        type: string
+                        $ref: '#/components/schemas/Control'
+                    description: List of sub - controls -     this is in accordance with the OSCAL model.
+                metrics:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Metric'
+                    description: metrics contains either a list of reference to metrics - in this case only the id field of the metric is populated - or a list of populated metric meta-data, most likely returned by the database.
+                parentControlId:
+                    type: string
+                    description: Reference to the parent category this control belongs to.
+                parentControlCategoryName:
+                    type: string
+                parentControlCategoryCatalogId:
+                    type: string
+                assuranceLevel:
+                    type: string
+                    description: An assurance level is not offered by every catalog, therefore it is optional.
+            description: Control represents a certain Control that needs to be fulfilled. It could be a Control in a certification catalog. It follows the OSCAL model. A requirement in the EUCS terminology, e.g., is represented as the lowest sub-control.
+        ControlInScope:
+            type: object
+            properties:
+                targetOfEvaluationCloudServiceId:
+                    type: string
+                targetOfEvaluationCatalogId:
+                    type: string
+                controlId:
+                    type: string
+                controlCategoryName:
+                    type: string
+                controlCategoryCatalogId:
+                    type: string
+                monitoringStatus:
+                    enum:
+                        - MONITORING_STATUS_UNSPECIFIED
+                        - MONITORING_STATUS_AUTOMATICALLY_MONITORED
+                        - MONITORING_STATUS_MANUALLY_MONITORED
+                        - MONITORING_STATUS_DELEGATED
+                    type: string
+                    format: enum
+            description: ControlInScope defines a control which is "in scope" of a target of evaluation. Additional meta-data can be defined when a control is in scope, e.g., its monitoring status (continuously monitored, delegated, etc.)
+        Dependency:
+            type: object
+            properties:
+                path:
+                    type: string
+                version:
+                    type: string
+        GetCloudServiceStatisticsResponse:
+            type: object
+            properties:
+                numberOfDiscoveredResources:
+                    type: integer
+                    description: number of discovered resources per cloud service
+                    format: int64
+                numberOfAssessmentResults:
+                    type: integer
+                    description: number of assessment results per cloud service
+                    format: int64
+                numberOfEvidences:
+                    type: integer
+                    description: number of evidences per cloud service
+                    format: int64
+                numberOfSelectedCatalogs:
+                    type: integer
+                    description: number of selected catalogs per cloud service
+                    format: int64
         GoogleProtobufAny:
             type: object
             properties:
@@ -926,6 +1797,15 @@ components:
                         $ref: '#/components/schemas/AssessmentTool'
                 nextPageToken:
                     type: string
+        ListCatalogsResponse:
+            type: object
+            properties:
+                catalogs:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Catalog'
+                nextPageToken:
+                    type: string
         ListCertificatesResponse:
             type: object
             properties:
@@ -944,6 +1824,24 @@ components:
                         $ref: '#/components/schemas/CloudService'
                 nextPageToken:
                     type: string
+        ListControlsInScopeResponse:
+            type: object
+            properties:
+                controlsInScope:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/ControlInScope'
+                nextPageToken:
+                    type: string
+        ListControlsResponse:
+            type: object
+            properties:
+                controls:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Control'
+                nextPageToken:
+                    type: string
         ListMetricConfigurationResponse:
             type: object
             properties:
@@ -961,13 +1859,22 @@ components:
                         $ref: '#/components/schemas/Metric'
                 nextPageToken:
                     type: string
-        ListRequirementsResponse:
+        ListPublicCertificatesResponse:
+            type: object
+            properties:
+                certificates:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Certificate'
+                nextPageToken:
+                    type: string
+        ListTargetsOfEvaluationResponse:
             type: object
             properties:
-                requirements:
+                targetOfEvaluation:
                     type: array
                     items:
-                        $ref: '#/components/schemas/Requirement'
+                        $ref: '#/components/schemas/TargetOfEvaluation'
                 nextPageToken:
                     type: string
         Metric:
@@ -1000,6 +1907,8 @@ components:
                     type: integer
                     description: The interval in seconds the evidences must be collected for the respective metric. For now, we are not able to use google.protobuf.Duration because it is converted to a custom object in OpenAPI (https://github.com/google/gnostic/issues/351)
                     format: int64
+                implementation:
+                    $ref: '#/components/schemas/MetricImplementation'
             description: A metric resource
         MetricConfiguration:
             type: object
@@ -1016,6 +1925,12 @@ components:
                     type: string
                     description: The last time of update
                     format: date-time
+                metricId:
+                    type: string
+                    description: The metric this configuration belongs to
+                cloudServiceId:
+                    type: string
+                    description: The service this configuration belongs to
             description: Defines the operator and a target value for an individual metric
         MetricImplementation:
             type: object
@@ -1026,7 +1941,7 @@ components:
                 lang:
                     enum:
                         - LANGUAGE_UNSPECIFIED
-                        - REGO
+                        - LANGUAGE_REGO
                     type: string
                     description: The language this metric is implemented in
                     format: enum
@@ -1068,24 +1983,30 @@ components:
                 minMax:
                     $ref: '#/components/schemas/MinMax'
             description: A range resource representing the range of values
-        Requirement:
+        Runtime:
             type: object
             properties:
-                id:
+                releaseVersion:
                     type: string
-                name:
+                    description: release_version is the latest Clouditor release version for this commit
+                vcs:
                     type: string
-                description:
+                    description: vcs is the used version control system
+                commitHash:
                     type: string
-                metrics:
+                    description: commit_hash is the current Clouditor commit hash
+                commitTime:
+                    type: string
+                    description: commit_time is the time of the Clouditor commit
+                    format: date-time
+                golangVersion:
+                    type: string
+                    description: golang_version is the used golang version
+                dependencies:
                     type: array
                     items:
-                        $ref: '#/components/schemas/Metric'
-                    description: metrics contains either a list of reference to metrics - in this case only the id field of the metric is populated - or a list of populated metric meta-data, most likely returned by the database.
-                category:
-                    type: string
-                    description: category can be used to categorize requirements, e.g., according to a specific security catalog or something that groups requirements together, e.g., a security control.
-            description: Requirement represents a certain requirement that needs to be fulfilled. It could be a control in a certification catalog.
+                        $ref: '#/components/schemas/Dependency'
+                    description: dependency is a list of used runtime dependencies
         State:
             type: object
             properties:
@@ -1119,11 +2040,24 @@ components:
                     description: A list of messages that carry the error details.  There is a common set of message types for APIs to use.
             description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).'
         StoreAssessmentResultResponse:
+            type: object
+            properties: {}
+            description: StoreAssessmentResultReponse belongs to StoreAssessmentResult, which uses a custom unary RPC and therefore requires a response message according to the style convention. Since no return values are required, this is empty.
+        TargetOfEvaluation:
             type: object
             properties:
-                status:
-                    type: boolean
-                statusMessage:
+                cloudServiceId:
+                    type: string
+                catalogId:
                     type: string
+                assuranceLevel:
+                    type: string
+                    description: an assurance level is not offered by every catalog, therefore it is optional
+                controlsInScope:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Control'
+                    description: 'the controls that are in scope of this ToE. Note: For some security catalogs, e.g., the EUCS, a specific set of controls (in the "worst case": all) are automatically in scope. In this case, this list needs auto-filled at an appropriate time, e.g,. in CreateTargetOfEvaluation. Note: Because of limitations of our ORM framework, this field only contains a list of controls that are in scope of the target, but not the actual meta-data associated it with it (which is of message type ControlInScope). In order to retrieve the meta-data of the controls, the RPC ListControlsInScope (or the associated REST path) must be called.'
+            description: A Target of Evaluation binds a cloud service to a catalog, so the service is evaluated regarding this catalog's controls
 tags:
     - name: Orchestrator
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/AuthTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/AuthTest.kt
index c41601b4ef8713fd84167adeaf2c077f42728d9e..c2c06d78505cf0e6dbbb30b3eae330c060578e27 100644
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/AuthTest.kt
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/AuthTest.kt
@@ -28,6 +28,7 @@
  */
 package de.fraunhofer.aisec.codyze.medina
 
+import de.fraunhofer.aisec.codyze.medina.connection.Connection
 import java.time.OffsetDateTime
 import java.util.*
 import org.junit.jupiter.api.Assertions
@@ -44,6 +45,7 @@ class AuthTest {
 
     /**
      * Tests the OAuth connection to a local instance of the Orchestrator
+     *
      * @author Florian Wendland
      * @author Robert Haimerl
      */
@@ -52,8 +54,8 @@ class AuthTest {
     fun testLocalAuthConnection() {
         val connection = Connection(orchestratorURL, tokenURL, testUsername, testPassword)
 
-        Assertions.assertTrue(connection.testOAuthConnection())
-        Assertions.assertTrue(connection.testOrchestratorConnection())
+        Assertions.assertTrue(connection.getOAuthManager().testOAuthConnection())
+        Assertions.assertTrue(connection.getApiManager().testOrchestratorConnection())
 
         // create a MetricConfiguration (used for both AssessmentResults below)
         val mConfig = MetricConfiguration()
@@ -84,11 +86,12 @@ class AuthTest {
         compAR.nonComplianceComments = "does not comply"
 
         val resultsToSend = arrayOf(nCompAR, compAR)
-        Assertions.assertTrue(connection.sendAssessmentResults(resultsToSend))
+        Assertions.assertTrue(connection.getApiManager().sendAssessmentResults(resultsToSend))
     }
 
     /**
      * Tests the OAuth connection to a remote instance of the Orchestrator
+     *
      * @author Florian Wendland
      */
     @Disabled
@@ -101,6 +104,6 @@ class AuthTest {
 
         val connection = Connection("", remoteTokenURI, username, password)
 
-        Assertions.assertTrue(connection.testOAuthConnection())
+        Assertions.assertTrue(connection.getOAuthManager().testOAuthConnection())
     }
 }
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CliTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CliTest.kt
index b2e856614491a98660724717d8f9da5795545e6b..fae7b2fbac9be016bc6eaff8f33bca352be87f24 100644
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CliTest.kt
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CliTest.kt
@@ -28,6 +28,7 @@
  */
 package de.fraunhofer.aisec.codyze.medina
 
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
 import org.junit.jupiter.api.Assertions
 import org.junit.jupiter.api.Test
 import picocli.CommandLine
@@ -36,12 +37,15 @@ class CliTest {
 
     /**
      * Tests the fail response on a missing OAuth-Endpoint
+     *
      * @author Florian Wendland
      */
     @Test
     fun missingOAuth() {
         val args =
             arrayOf(
+                "--id",
+                "cli-test",
                 "--username",
                 "user",
                 "--password",
@@ -57,12 +61,15 @@ class CliTest {
 
     /**
      * Tests the fail response on a missing OAuth-Username
+     *
      * @author Florian Wendland
      */
     @Test
     fun missingUsername() {
         val args =
             arrayOf(
+                "--id",
+                "cli-test",
                 "--oauth-endpoint",
                 "https://localhost:8080/",
                 "--password",
@@ -78,12 +85,15 @@ class CliTest {
 
     /**
      * Tests the fail response on a missing Orchestrator-Endpoint
+     *
      * @author Florian Wendland
      */
     @Test
     fun missingOrchestrator() {
         val args =
             arrayOf(
+                "--id",
+                "cli-test",
                 "--oauth-endpoint",
                 "https://localhost:8080/",
                 "--username",
@@ -97,14 +107,41 @@ class CliTest {
         Assertions.assertThrows(CommandLine.ParameterException::class.java) { config.validate() }
     }
 
+    /**
+     * Tests the fail response on a missing cloudServiceId
+     *
+     * @author Robert Haimerl
+     */
+    @Test
+    fun missingId() {
+        val args =
+            arrayOf(
+                "--oauth-endpoint",
+                "https://localhost:8080/",
+                "--username",
+                "user",
+                "--password",
+                "pw",
+                "--endpoint",
+                "http://localhost:8080/"
+            )
+
+        val config = Configuration()
+        CommandLine(config).parseArgs(*args)
+        Assertions.assertThrows(CommandLine.ParameterException::class.java) { config.validate() }
+    }
+
     /**
      * Tests the response for a valid configuration
+     *
      * @author Florian Wendland
      */
     @Test
     fun valid() {
         val args =
             arrayOf(
+                "--id",
+                "cli-test",
                 "--oauth-endpoint",
                 "https://localhost:8080/",
                 "--username",
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeCliPassThroughTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeCliPassThroughTest.kt
index af5d0ea6e5dd50643811a8cbbb3b6dd9ce839092..009a68e19a45b1e498f2330e269c14dab54759f1 100644
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeCliPassThroughTest.kt
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeCliPassThroughTest.kt
@@ -28,6 +28,7 @@
  */
 package de.fraunhofer.aisec.codyze.medina
 
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
 import java.io.File
 import org.junit.jupiter.api.Assertions
 import org.junit.jupiter.api.Test
@@ -36,12 +37,15 @@ import picocli.CommandLine
 class CodyzeCliPassThroughTest {
     /**
      * Tests whether cli args are correctly passed on to codyze
+     *
      * @author Florian Wendland
      */
     @Test
     fun cliArgs() {
         val args =
             arrayOf(
+                "--id",
+                "cli-pass-through-test",
                 "--username",
                 "user",
                 "--password",
@@ -62,6 +66,7 @@ class CodyzeCliPassThroughTest {
 
     /**
      * Tests whether config file args are correctly passed on to codyze
+     *
      * @author Florian Wendland
      */
     @Test
@@ -71,8 +76,9 @@ class CodyzeCliPassThroughTest {
                     CodyzeCliPassThroughTest::class
                         .java
                         .classLoader
-                        .getResource("codyze.yaml")!!
-                        .toURI()
+                        .getResource("codyze.yaml")
+                        ?.toURI()
+                        ?: Assertions.fail("Could not load codyze.yaml")
                 )
                 .toPath()
                 .toString()
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConfigurationTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConfigurationTest.kt
index 87797aada70d4e8865b303ad0104f1c8398019f5..02f460768aacb8129fbc79e1dfadd6edc87958c7 100644
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConfigurationTest.kt
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConfigurationTest.kt
@@ -28,6 +28,7 @@
  */
 package de.fraunhofer.aisec.codyze.medina
 
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
 import org.junit.jupiter.api.Assertions
 import org.junit.jupiter.api.Test
 import picocli.CommandLine
@@ -36,6 +37,7 @@ class ConfigurationTest {
 
     /**
      * Tests parsing of the cli arguments
+     *
      * @author Florian Wendland
      */
     @Test
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConverterTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConverterTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c50fbcd59525f71079694eea48a5b33876d20572
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConverterTest.kt
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import de.fraunhofer.aisec.codyze.analysis.Finding
+import de.fraunhofer.aisec.codyze.medina.assembling.Assembler
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
+import de.fraunhofer.aisec.codyze.medina.mapping.parseMapping
+import de.fraunhofer.aisec.codyze.sarif.schema.Result
+import de.fraunhofer.aisec.mark.markDsl.Action
+import java.io.File
+import java.net.URI
+import java.nio.file.Path
+import kotlin.io.path.Path
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+
+class ConverterTest {
+    /**
+     * Tests the conversion from Findings to compliant AssessmentResults
+     *
+     * @author Robert Haimerl
+     */
+    @Test
+    fun testCompliant() {
+        // create some Finding object
+        val logMsg = "Variable cm not initialized"
+        val artifactUri = URI.create("file:///tmp/test.cpp")
+        val id = "WrongUseOfBotan_CipherMode"
+        val kind = Result.Kind.PASS
+        val f1 = Finding(id, Action.INFO, logMsg, artifactUri, listOf(), kind)
+
+        val assembler =
+            Assembler(Environment(Configuration.CIEnvironment.NONE, Path.of("")), "test-id")
+        val assessmentResults =
+            assembler.convertFindingsToAssessmentResults(
+                parseMapping(File("src/test/resources/Mark/exampleMapping.yaml"))!!,
+                setOf(f1),
+                "",
+                "123"
+            )
+
+        assertNotNull(assessmentResults)
+        assertEquals(1, assessmentResults.size)
+        val result = assessmentResults.first()
+        assertTrue(result.compliant ?: false)
+        assertEquals(arrayListOf(1.23, 3.14), result.metricConfiguration!!.targetValue)
+        assertEquals("", result.nonComplianceComments)
+        assertEquals("TestMetric1", result.metricId)
+    }
+
+    /**
+     * Tests the conversion from Findings to non-compliant AssessmentResults
+     *
+     * @author Robert Haimerl
+     */
+    @Test
+    fun testNonCompliant() {
+        // create some Finding object
+        val logMsg = "You should not do that"
+        val artifactUri = URI.create("file:///tmp/test.cpp")
+        val id = "VariableNotInitialized"
+        val kind = Result.Kind.FAIL
+        val f2 = Finding(id, Action.FAIL, logMsg, artifactUri, listOf(), kind)
+
+        val assembler =
+            Assembler(Environment(Configuration.CIEnvironment.NONE, Path("")), "test-id")
+        val assessmentResults =
+            assembler.convertFindingsToAssessmentResults(
+                parseMapping(File("src/test/resources/Mark/exampleMapping.yaml"))!!,
+                setOf(f2),
+                "",
+                "123"
+            )
+
+        assertNotNull(assessmentResults)
+        assertEquals(1, assessmentResults.size)
+        val result = assessmentResults.first()
+        assertTrue(!(result.compliant ?: true))
+        assertEquals(true, result.metricConfiguration!!.targetValue)
+        assertEquals("${f2.identifier}: ${f2.logMsg}\n", result.nonComplianceComments)
+        assertEquals("TestMetric2", result.metricId)
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/DemoTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/DemoTest.kt
index cd62e0bfbc5f4ccf9b725813bcdb85123b293896..58dcdc2ce993f902aad7442895d95efa91648363 100644
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/DemoTest.kt
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/DemoTest.kt
@@ -36,6 +36,7 @@ class DemoTest {
 
     /**
      * Implements a demonstration printing the findings of the TlsServer.java class
+     *
      * @author Florian Wendland
      */
     @Test
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/IntegrationTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/IntegrationTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..53080b34866d0b1a9cb4b8916e41889c3fd8c614
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/IntegrationTest.kt
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022-2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import de.fraunhofer.aisec.codyze.analysis.AnalysisServer
+import de.fraunhofer.aisec.codyze.medina.assembling.Assembler
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.connection.Connection
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
+import java.io.File
+import java.util.*
+import java.util.concurrent.TimeUnit
+import kotlin.io.path.Path
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import org.openapitools.client.orchestrator.model.AssessmentResult
+import org.openapitools.client.orchestrator.model.MetricConfiguration
+import picocli.CommandLine
+
+class IntegrationTest {
+
+    private val args = arrayOf("--config", "src/test/resources/codyze-test.yaml")
+
+    @Test
+    @Disabled
+    fun testCodyzeIntegration() {
+        // 1 ----------- Test Configuration
+        // parse configuration
+        val config = Configuration.initialize(args)
+        CommandLine(config).setUnmatchedArgumentsAllowed(true).parseArgs(*args)
+        // try to convert to codyze config
+        val codyzeConfig =
+            de.fraunhofer.aisec.codyze.config.Configuration.initConfig(
+                config.configFile.path.toFile(),
+                *args,
+                "--default-passes",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.IdentifierPass",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.EdgeCachePass"
+            )
+        // assert that the specified configuration was taken over to the codyzeConfig
+        Assertions.assertTrue(
+            Path(codyzeConfig.output).endsWith(Path("src/test/resources/codyze.sarif"))
+        )
+        Assertions.assertEquals(1, codyzeConfig.source.size)
+        Assertions.assertTrue(
+            codyzeConfig.source[0].endsWith(File("src/test/resources/exampleFiles/2_1_2_1_02.cpp"))
+        )
+        Assertions.assertTrue(codyzeConfig.sarifOutput)
+
+        // 2 ----------- Test Server Creation
+        val server = AnalysisServer(codyzeConfig)
+        server.start()
+        // assert that the server could be initialized
+        Assertions.assertNotNull(server)
+
+        // 3 ----------- Test Analysis
+        val findings =
+            server.analyze(codyzeConfig.source)[codyzeConfig.timeout, TimeUnit.MINUTES].findings
+        // assert if findings are as expected
+        Assertions.assertEquals(3, findings.size)
+        Assertions.assertTrue(findings.stream().anyMatch { it.identifier == "Cipher_Mode_Order" })
+        Assertions.assertTrue(findings.stream().anyMatch { it.identifier == "RNGOrder" })
+        Assertions.assertTrue(
+            findings.stream().anyMatch { it.identifier == "WrongUseOfBotan_CipherMode" }
+        )
+    }
+
+    /**
+     * To successfully run this test, the CODYZE_PWD environment variable needs to be set to the
+     * OAuth2 password for the Orchestrator
+     */
+    @Test
+    @Disabled
+    fun testOrchestratorIntegration() {
+        // create codyzeConfig
+        val config = Configuration.initialize(args)
+        CommandLine(config).setUnmatchedArgumentsAllowed(true).parseArgs(*args)
+        val codyzeConfig =
+            de.fraunhofer.aisec.codyze.config.Configuration.initConfig(
+                config.configFile.path.toFile(),
+                *args,
+                "--default-passes",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.IdentifierPass",
+                "--passes+",
+                "de.fraunhofer.aisec.cpg.passes.EdgeCachePass"
+            )
+
+        // 1 ----------- Test Connection Creation
+        val connection =
+            Connection(
+                config.orchestrator.orchestratorEndpoint.toString(),
+                config.orchestrator.auth.oauthEndpoint.toString(),
+                config.orchestrator.auth.username,
+                config.orchestrator.auth.password
+            )
+        // assert that Object could be created and connection is possible
+        Assertions.assertNotNull(connection)
+        Assertions.assertTrue(connection.getOAuthManager().testOAuthConnection())
+
+        // 2 ----------- Test Evidence Storage
+        val assembler =
+            Assembler(Environment(Configuration.CIEnvironment.NONE, Path("")), "integration-test")
+        val evidence = assembler.createEvidence(Path(codyzeConfig.output))
+        evidence.resource =
+            object {
+                @Suppress("unused") val id = "123"
+            }
+        // assert that evidence could be stored without problems
+        Assertions.assertTrue(connection.getApiManager().storeEvidence(evidence))
+
+        // 3 ----------- Test Result Storage
+        // create an AssessmentResult
+        val ar = AssessmentResult()
+        val mc = MetricConfiguration()
+
+        mc.isDefault = true
+        mc.operator = "="
+        mc.targetValue = 5
+        ar.metricConfiguration = mc
+        ar.id = UUID.randomUUID().toString()
+        ar.timestamp = java.time.OffsetDateTime.now()
+        ar.evidenceId = evidence.id
+        ar.resourceId = "123"
+        ar.metricId = "test-metric"
+        ar.compliant = false
+        ar.nonComplianceComments = ""
+
+        // assert that this result could be stored without problems
+        Assertions.assertTrue(connection.getApiManager().sendAssessmentResults(arrayOf(ar)))
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/LoggingTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/LoggingTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a5641c3f8b9cba859ad2b0bb442ae2491cfb50e7
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/LoggingTest.kt
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import io.github.oshai.kotlinlogging.KotlinLogging
+import java.io.File
+import org.apache.log4j.AppenderSkeleton
+import org.apache.log4j.LogManager
+import org.apache.log4j.spi.LoggingEvent
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.io.TempDir
+
+class LoggingTest {
+    private val logger = KotlinLogging.logger("TestLogger")
+    @TempDir private var logFile = File("test-log.log")
+
+    @Test
+    fun testFileCreation() {
+        logger.info { "Testing file creation" }
+        Assertions.assertTrue(logFile.exists())
+    }
+
+    @Test
+    fun testLogContents() {
+        val logImpl = LogManager.getLogger("TestLogger")
+        val appender = TestAppender()
+        logImpl.addAppender(appender)
+
+        logImpl.debug("debug")
+        logImpl.info("info")
+        logImpl.warn("warn")
+        logImpl.error("error")
+
+        logImpl.removeAppender(appender)
+        val logLines = appender.getLog().map { event -> event.renderedMessage }
+
+        // test whether log content is as specified
+        Assertions.assertEquals("warn", logLines[0])
+        Assertions.assertEquals("error", logLines[1])
+    }
+
+    // create new Appender just for testing the logging
+    internal class TestAppender : AppenderSkeleton() {
+        private val log: MutableList<LoggingEvent> = ArrayList<LoggingEvent>()
+
+        override fun requiresLayout(): Boolean {
+            return false
+        }
+
+        override fun append(loggingEvent: LoggingEvent) {
+            log.add(loggingEvent)
+        }
+
+        override fun close() {}
+
+        fun getLog(): List<LoggingEvent> {
+            return ArrayList<LoggingEvent>(log)
+        }
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MappingTreeTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MappingTreeTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d947213ce45e6070435e9eb7bbb8fb0e8327d24d
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MappingTreeTest.kt
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import de.fraunhofer.aisec.codyze.medina.mapping.parseMappingTree
+import java.io.File
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class MappingTreeTest {
+    /**
+     * Tests whether the tree structure of the directory gets resolved correctly
+     *
+     * @author Robert Haimerl
+     */
+    @Test
+    fun testTreeParsing() {
+        val mappingMap = parseMappingTree(File("src/test/resources/mappingTreeTestStructure"))
+        Assertions.assertEquals(3, mappingMap.size)
+        val directoryArray =
+            setOf(
+                File("src/test/resources/mappingTreeTestStructure/botan"),
+                File("src/test/resources/mappingTreeTestStructure/bouncycastle/bc1"),
+                File("src/test/resources/mappingTreeTestStructure/bouncycastle/bc2")
+            )
+        Assertions.assertEquals(directoryArray, mappingMap.values.toSet())
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MedinaMetrikTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MedinaMetrikTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..beabf0749327c26c4da899c0706d5e242761a95f
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MedinaMetrikTest.kt
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import de.fraunhofer.aisec.codyze.medina.assembling.Environment
+import de.fraunhofer.aisec.codyze.medina.evaluation.base.ApprovedCommitAuthorEvaluator
+import de.fraunhofer.aisec.codyze.medina.main.Configuration
+import kotlin.io.path.Path
+import org.junit.jupiter.api.Test
+
+class MedinaMetrikTest {
+
+    @Test
+    fun testCommitAuthor() {
+        val env = Environment(Configuration.CIEnvironment.NONE, Path("./"))
+        val eval = ApprovedCommitAuthorEvaluator(env, java.nio.file.Path.of("."))
+
+        eval.evaluate(arrayOf())
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ParserTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ParserTest.kt
deleted file mode 100644
index 7925c56150c1a7e65d1808d453c6bd28ef3c95db..0000000000000000000000000000000000000000
--- a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ParserTest.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-/* 
- * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- *      https://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *     _____          _
- *    / ____|        | | 
- *   | |     ___   __| |_   _ _______
- *   | |    / _ \ / _` | | | |_  / _ \
- *   | |___| (_) | (_| | |_| |/ /  __/
- *    \_____\___/ \__,_|\__, /___\___|
- *                      __/ |
- *                     |___/
- * 
- * This file is part of the MEDINA Framework.
- */
-package de.fraunhofer.aisec.codyze.medina
-
-import de.fraunhofer.aisec.codyze.analysis.Finding
-import de.fraunhofer.aisec.codyze.medina.util.findingToAR
-import de.fraunhofer.aisec.codyze.sarif.schema.Result
-import de.fraunhofer.aisec.cpg.sarif.Region
-import de.fraunhofer.aisec.mark.markDsl.Action
-import java.net.URI
-import org.junit.jupiter.api.Assertions.*
-import org.junit.jupiter.api.Disabled
-import org.junit.jupiter.api.Test
-
-class ParserTest {
-    /**
-     * Tests the conversion from Findings to compliant AssessmentResults
-     * @author Robert Haimerl
-     */
-    @Disabled
-    @Test
-    fun testCompliant() {
-        // create some Finding object
-        val logMsg = "Variable cm not initialized"
-        val artifactUri = URI.create("file:///tmp/test.cpp")
-        val id = "WrongUseOfBotan_CipherMode"
-        val regions = listOf(Region(0, 2, 10, 12))
-        val kind = Result.Kind.PASS
-        val f1 = Finding(id, Action.INFO, logMsg, artifactUri, regions, kind)
-
-        val assessmentResult = findingToAR(f1, "")
-
-        assertNotNull(assessmentResult)
-        assertTrue(assessmentResult?.compliant ?: false)
-        assertEquals(f1.identifier, assessmentResult?.metricId, "Wrong metricID")
-        assertEquals(
-            f1.logMsg,
-            assessmentResult?.nonComplianceComments,
-            "The comment for non-compliance differs from the logMsg"
-        )
-    }
-
-    /**
-     * Tests the conversion from Findings to non-compliant AssessmentResults
-     * @author Robert Haimerl
-     */
-    @Disabled
-    @Test
-    fun testNonCompliant() {
-        // create some Finding object
-        val logMsg = "You should not do that"
-        val artifactUri = URI.create("file:///tmp/test.cpp")
-        val id = "VariableNotInitialized"
-        val regions = listOf(Region(0, 2, 10, 12))
-        val kind = Result.Kind.FAIL
-        val f2 = Finding(id, Action.FAIL, logMsg, artifactUri, regions, kind)
-
-        val assessmentResult = findingToAR(f2, "")
-
-        assertNotNull(assessmentResult)
-        assertFalse(assessmentResult?.compliant ?: false)
-        assertEquals(f2.identifier, assessmentResult?.metricId, "Wrong metricID")
-        assertEquals(
-            f2.logMsg,
-            assessmentResult?.nonComplianceComments,
-            "The comment for non-compliance differs from the logMsg"
-        )
-    }
-}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/SarifTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/SarifTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dffd97e1bbdc06ec6b5470068d228c8c2ea44536
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/SarifTest.kt
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina
+
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
+import de.fraunhofer.aisec.codyze.medina.util.amendExistingReport
+import de.fraunhofer.aisec.codyze.medina.util.codyzeMedinaComponent
+import io.github.detekt.sarif4k.*
+import kotlin.io.path.exists
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class SarifTest {
+
+    private val sampleReport =
+        SarifSchema210(
+            schema =
+                "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+            version = Version.The210,
+            runs =
+                listOf(
+                    Run(
+                        tool =
+                            Tool(
+                                driver =
+                                    ToolComponent(
+                                        name = "test-driver",
+                                    ),
+                                extensions = listOf()
+                            ),
+                        results =
+                            listOf(
+                                Result(message = Message(text = "this is result 1")),
+                                Result(message = Message(text = "this is result 2")),
+                                Result(message = Message(text = "this is result 3"))
+                            )
+                    )
+                )
+        )
+
+    @Test
+    fun testAmend() {
+        val evaluationResults: Map<Rule, EvaluationResult> =
+            mapOf(
+                Rule.CodeSignoff to
+                    EvaluationResult(true, "valid sign-off", "valid sign-off by xyz"),
+                Rule.ApprovedCommitAuthor to
+                    EvaluationResult(false, "wrong commit author", "commit authored by xyz")
+            )
+
+        val reportPath = kotlin.io.path.createTempFile()
+        val targetPath = kotlin.io.path.createTempFile()
+
+        reportPath.writeText(SarifSerializer.toJson(sampleReport))
+        amendExistingReport(reportPath, targetPath, evaluationResults)
+        val newReport = SarifSerializer.fromJson(targetPath.readText())
+
+        // Whether the old report was deleted
+        Assertions.assertFalse(reportPath.exists(), "The old report was not deleted after amending")
+        // Whether all results are present
+        Assertions.assertArrayEquals(
+            listOf(
+                    Result(message = Message(text = "this is result 1")),
+                    Result(message = Message(text = "this is result 2")),
+                    Result(message = Message(text = "this is result 3")),
+                    Result(
+                        message = Message(text = Rule.CodeSignoff.description),
+                        ruleID = Rule.CodeSignoff.id,
+                        kind = ResultKind.Pass
+                    ),
+                    Result(
+                        message = Message(text = Rule.ApprovedCommitAuthor.description),
+                        ruleID = Rule.ApprovedCommitAuthor.id,
+                        kind = ResultKind.Fail
+                    )
+                )
+                .toTypedArray(),
+            newReport.runs[0].results!!.toTypedArray(),
+            "The results of the new report are not as expected"
+        )
+        // Whether the extension was entered correctly
+        Assertions.assertEquals(
+            "test-driver",
+            newReport.runs[0].tool.extensions!![0].name,
+            "The old driver was not added as an extension"
+        )
+        Assertions.assertEquals(
+            codyzeMedinaComponent,
+            newReport.runs[0].tool.driver,
+            "The new driver was not set correctly"
+        )
+    }
+}
diff --git a/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/AssemblerTest.kt b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/AssemblerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2fbc2d355ebf35ca2c4c499329bf0573ff1473c6
--- /dev/null
+++ b/src/test/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/AssemblerTest.kt
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: Apache-2.0
+
+/* 
+ * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *     _____          _
+ *    / ____|        | | 
+ *   | |     ___   __| |_   _ _______
+ *   | |    / _ \ / _` | | | |_  / _ \
+ *   | |___| (_) | (_| | |_| |/ /  __/
+ *    \_____\___/ \__,_|\__, /___\___|
+ *                      __/ |
+ *                     |___/
+ * 
+ * This file is part of the MEDINA Framework.
+ */
+package de.fraunhofer.aisec.codyze.medina.assembling
+
+import de.fraunhofer.aisec.codyze.analysis.Finding
+import de.fraunhofer.aisec.codyze.medina.evaluation.EvaluationResult
+import de.fraunhofer.aisec.codyze.medina.evaluation.Rule
+import de.fraunhofer.aisec.codyze.medina.mapping.Configuration
+import de.fraunhofer.aisec.codyze.medina.mapping.Mapping
+import de.fraunhofer.aisec.codyze.medina.mapping.Metric
+import de.fraunhofer.aisec.codyze.medina.mapping.Type
+import java.time.OffsetDateTime
+import java.util.*
+import kotlin.io.path.Path
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.jupiter.MockitoExtension
+
+@ExtendWith(MockitoExtension::class)
+class AssemblerTest {
+
+    private val nullId = UUID(0, 0)
+    private val tool = "codyze-medina"
+    private val gitHash = "ffffff"
+
+    @Mock private lateinit var environment: Environment
+
+    private lateinit var assembler: Assembler
+
+    @BeforeEach
+    fun inject() {
+        assembler = Assembler(environment, nullId.toString())
+    }
+
+    @Test
+    fun testCreateEvidenceFromResultFile() {
+        `when`(environment.getGitHash()).thenReturn(gitHash)
+
+        val currentTime = OffsetDateTime.now()
+        val evidence = assembler.createEvidence(Path("invalid_path"))
+
+        // Assert that the timestamp is exact +- 5 seconds
+        assertTrue(evidence.timestamp?.isAfter(currentTime.minusSeconds(5)) ?: false)
+        assertTrue(evidence.timestamp?.isBefore(currentTime.plusSeconds(5)) ?: false)
+        // Assert that the cloud service id was correctly set
+        assertEquals(nullId.toString(), evidence.cloudServiceId)
+        // Assert that the tool id contains "codyze-medina"
+        assertTrue(evidence.toolId?.contains(tool, ignoreCase = true) ?: false)
+
+        verify(environment).getGitHash()
+
+        assertTrue(true)
+    }
+
+    @Test
+    fun testConvertEvaluationToAssessmentResult() {
+        val rule = Rule.CodeSignoff
+        val detailedMessage = "detailed-message"
+        val validated = false
+        val evaluationResult = EvaluationResult(validated, "message", detailedMessage)
+        val hash = gitHash
+        val evidenceId = nullId.toString()
+
+        val currentTime = OffsetDateTime.now()
+        val assessmentResult =
+            assembler.convertEvaluationToAssessmentResult(rule, evaluationResult, evidenceId, hash)
+
+        assertEquals(rule.operator, assessmentResult.metricConfiguration?.operator)
+        assertEquals(rule.targetValue, assessmentResult.metricConfiguration?.targetValue)
+        assertTrue(assessmentResult.metricConfiguration?.isDefault ?: false)
+        assertTrue(
+            assessmentResult.metricConfiguration?.updatedAt?.isAfter(currentTime.minusSeconds(5))
+                ?: false
+        )
+        assertTrue(
+            assessmentResult.metricConfiguration?.updatedAt?.isBefore(currentTime.plusSeconds(5))
+                ?: false
+        )
+        assertEquals(rule.name, assessmentResult.metricConfiguration?.metricId)
+        assertEquals(nullId.toString(), assessmentResult.metricConfiguration?.cloudServiceId)
+
+        assertTrue(assessmentResult.timestamp?.isAfter(currentTime.minusSeconds(5)) ?: false)
+        assertTrue(assessmentResult.timestamp?.isBefore(currentTime.plusSeconds(5)) ?: false)
+        assertEquals(evidenceId, assessmentResult.evidenceId)
+        assertEquals(hash, assessmentResult.resourceId)
+        assertEquals(rule.name, assessmentResult.metricId)
+        assertEquals(validated, assessmentResult.compliant)
+        assertEquals(nullId.toString(), assessmentResult.cloudServiceId)
+    }
+
+    @Test
+    fun testCreateFindingsToAssessmentResult() {
+        val message = "message"
+        val rule = "test-rule"
+        val name = "test-metric"
+        val targetValue = true
+        val isDefault = false
+        val type = Type.BOOLEAN
+        val operator = "=="
+
+        // initialize a simple mapping
+        val configuration = Configuration()
+        configuration.default = isDefault
+        configuration.type = type
+        configuration.target = arrayOf(targetValue)
+        configuration.operator = operator
+        val metric = Metric()
+        metric.name = name
+        metric.configuration = configuration
+        metric.rules = arrayOf(rule)
+        val mapping = Mapping()
+        mapping.metrics = arrayOf(metric)
+
+        // create a finding with a rule id covered by the mapping
+        val finding = Finding(rule, null, message, null, 0, 0, 0, 0)
+        val currentTime = OffsetDateTime.now()
+
+        val assessmentResult =
+            assembler
+                .convertFindingsToAssessmentResults(
+                    mapping,
+                    setOf(finding),
+                    nullId.toString(),
+                    gitHash
+                )[0]
+
+        assertEquals(metric.configuration.operator, assessmentResult.metricConfiguration?.operator)
+        assertEquals(targetValue, assessmentResult.metricConfiguration?.targetValue)
+        assertEquals(isDefault, assessmentResult.metricConfiguration?.isDefault)
+        assertTrue(
+            assessmentResult.metricConfiguration?.updatedAt?.isAfter(currentTime.minusSeconds(5))
+                ?: false
+        )
+        assertTrue(
+            assessmentResult.metricConfiguration?.updatedAt?.isBefore(currentTime.plusSeconds(5))
+                ?: false
+        )
+        assertEquals(name, assessmentResult.metricConfiguration?.metricId)
+        assertEquals(nullId.toString(), assessmentResult.metricConfiguration?.cloudServiceId)
+
+        assertTrue(assessmentResult.timestamp?.isAfter(currentTime.minusSeconds(5)) ?: false)
+        assertTrue(assessmentResult.timestamp?.isBefore(currentTime.plusSeconds(5)) ?: false)
+        assertEquals(nullId.toString(), assessmentResult.evidenceId)
+        assertEquals(gitHash, assessmentResult.resourceId)
+        assertEquals(name, assessmentResult.metricId)
+        assertEquals(false, assessmentResult.compliant)
+        assertEquals(nullId.toString(), assessmentResult.cloudServiceId)
+    }
+}
diff --git a/src/test/resources/Mark/botan/mapping.yaml b/src/test/resources/Mark/botan/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..be0dd4b225c79d78c50954e79ad25b5f5e963f6f
--- /dev/null
+++ b/src/test/resources/Mark/botan/mapping.yaml
@@ -0,0 +1,29 @@
+metrics:
+  - name: "TestMetric1"
+    rules:
+      - "WrongUseOfBotan_CipherMode"
+    configuration:
+      default: false
+      operator: "=="
+      type: NUMBER
+      target:
+        - "1.23"
+        - "3.14"
+  - name: "TestMetric2"
+    rules:
+      - "RNGOrder"
+    configuration:
+      default: false
+      operator: "=="
+      type: BOOLEAN
+      target:
+        - "true"
+  - name: "TestMetric3"
+    rules:
+      - "Cipher_Mode_Order"
+    configuration:
+      default: false
+      operator: ">="
+      type: NUMBER
+      target:
+        - "2"
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/AlgorithmParameterGenerator.mark b/src/test/resources/Mark/bouncycastle/AlgorithmParameterGenerator.mark
deleted file mode 100644
index 6edb8a7d7dea2ca282c93702ba0f9461eab97146..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/AlgorithmParameterGenerator.mark
+++ /dev/null
@@ -1,43 +0,0 @@
-package java.jca
-
-entity AlgorithmParameterGenerator {
-	
-	var algorithm;
-	var provider;
-	var size;
-	var random;
-	var genParamSpec;
-	var params;
-	
-	
-	op instantiate {
-		java.security.AlgorithmParameterGenerator.getInstance(
-			algorithm : java.lang.String
-		);
-		java.security.AlgorithmParameterGenerator.getInstance(
-			algorithm : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op initialize {
-		java.security.AlgorithmParameterGenerator.init(
-			size : int
-		);
-		java.security.AlgorithmParameterGenerator.init(
-			size : int,
-			random : java.security.SecureRandom
-		);
-		java.security.AlgorithmParameterGenerator.init(
-			genParamSpec : java.security.spec.AlgorithmParameterSpec
-		);
-		java.security.AlgorithmParameterGenerator.init(
-			genParamSpec : java.security.spec.AlgorithmParameterSpec,
-			random : java.security.SecureRandom
-		);
-	}
-	
-	op generate {
-		params = java.security.AlgorithmParameterGenerator.generateParameters();
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/AlgorithmParameters.mark b/src/test/resources/Mark/bouncycastle/AlgorithmParameters.mark
deleted file mode 100644
index 268c9124dbe47e8f66397c5d947eea7a4f5d4673..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/AlgorithmParameters.mark
+++ /dev/null
@@ -1,34 +0,0 @@
-package java.jca
-
-entity AlgorithmParameters {
-	
-	var algorithm;
-	var provider;
-	var params;
-	var format;
-	var paramSpec;
-	
-	
-	op instantiate {
-		java.security.AlgorithmParameters.getInstance(
-			algorithm : java.lang.String
-		);
-		java.security.AlgorithmParameters.getInstance(
-			algorithm : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op initialize {
-		java.security.AlgorithmParameters.init(
-			params : byte[]
-		);
-		java.security.AlgorithmParameters.init(
-			params : byte[],
-			format : java.lang.String
-		);
-		java.security.AlgorithmParameters.init(
-			paramSpec : java.security.spec.AlgorithmParameterSpec
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/CertPathBuilder.mark b/src/test/resources/Mark/bouncycastle/CertPathBuilder.mark
deleted file mode 100644
index ec8c2645cd78f93ce5fc3f3b760b500bd6c20266..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/CertPathBuilder.mark
+++ /dev/null
@@ -1,24 +0,0 @@
-package jav.jca
-
-entity CertPathBuilder {
-	
-	var algorithm;
-	var provider;
-	var params;
-	
-	op instantiate {
-		java.security.cert.CertPathBuilder.getInstance(
-			algorithm : java.lang.String
-		);
-		java.security.cert.CertPathBuilder.getInstance(
-			algorithm : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op build {
-		java.security.cert.CertPathBuilder.build(
-			params : java.security.cert.CertPathParameters
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/CertPathValidator.mark b/src/test/resources/Mark/bouncycastle/CertPathValidator.mark
deleted file mode 100644
index 76dd8afbc24d843f530db748aefd3684a830aa96..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/CertPathValidator.mark
+++ /dev/null
@@ -1,28 +0,0 @@
-package java.jca
-
-entity CertPathValidator {
-	
-	var algorithm;
-	var provider;
-	
-	var certPath;
-	var params;
-	
-	
-	op instantiate {
-		java.security.cert.CertPathValidator.getInstance(
-			algorithm : java.lang.String
-		);
-		java.security.cert.CertPathValidator.getInstance(
-			algorithm : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op validate {
-		java.security.cert.CertPathValidator.validate(
-			certPath : java.security.cert.CertPath,
-			params : java.security.cert.CertPathParameters
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/CertStore.mark b/src/test/resources/Mark/bouncycastle/CertStore.mark
deleted file mode 100644
index 2e6af2ae5fc889f014d02b1bda671d617051c66d..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/CertStore.mark
+++ /dev/null
@@ -1,34 +0,0 @@
-package java.jca
-
-entity CertStore {
-	
-	var type;
-	var params;
-	var provider;
-	
-	var selector;
-	var certificates;
-	var crls;
-	
-	
-	op instantiate {
-		java.security.cert.CertStore.getInstance(
-			type : java.lang.String,
-			params : java.security.cert.CertStoreParameters
-		);
-		java.security.cert.CertStore.getInstance(
-			type : java.lang.String,
-			params : java.security.cert.CertStoreParameters,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op get {
-		certificates = java.security.cert.CertStore.getCertificates(
-			selector : java.security.cert.CertSelector
-		);
-		crls = java.security.cert.CertStore.getCRLs(
-			selector : java.security.cert.CRLSelector
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/CertificateFactory.mark b/src/test/resources/Mark/bouncycastle/CertificateFactory.mark
deleted file mode 100644
index dafaed2dd9e2bcfcd8b800104e5f0c39ef822fee..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/CertificateFactory.mark
+++ /dev/null
@@ -1,56 +0,0 @@
-package java.jca
-
-entity CertificateFactory {
-	
-	var type;
-	var provider;
-	
-	var inStream;
-	var certificate;
-	
-	var encoding;
-	var certificates;
-	var certpath;
-	
-	
-	op instantiate {
-		java.security.cert.CertificateFactory.getInstance(
-			type : java.lang.String
-		);
-		java.security.cert.CertificateFactory.getInstance(
-			type : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op generateCertificate {
-		certificate = java.security.cert.CertificateFactory.generateCertificate(
-			inStream : java.io.InputStream
-		);
-		certificates = java.security.cert.CertificateFactory.generateCertificates( // TODO how to denote that this returns Collection<? extends Certificate>
-			inStream : java.io.InputStream
-		);
-	}
-	
-	op generateCertPath {
-		certpath = java.security.cert.CertificateFactory.generateCertPath(
-			inStream : java.io.InputStream
-		);
-		certificates = java.security.cert.CertificateFactory.generateCertPath(
-			inStream : java.io.InputStream,
-			encoding : java.lang.String
-		);
-		certificates = java.security.cert.CertificateFactory.generateCertPath(
-			certificates : List/* <? extends Certificate> */ // TODO how to handle generic list? what is actually seen by cpg
-		);
-	}
-	
-	op generateCRL {
-		certificate = java.security.cert.CertificateFactory.generateCRL(
-			inStream : java.io.InputStream
-		);
-		certificates = java.security.cert.CertificateFactory.generateCRLs( // TODO how to denote that this returns Collection<? extends CRL>
-			inStream : java.io.InputStream
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ChaCha20ParameterSpec.mark b/src/test/resources/Mark/bouncycastle/ChaCha20ParameterSpec.mark
deleted file mode 100644
index a57fc83710fe71621d4043532dc902dfdf9dc17d..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ChaCha20ParameterSpec.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity ChaCha20ParameterSpec {
-
-	var nonce;
-	var counter;
-
-	op instantiate {
-		javax.crypto.spec.ChaCha20ParameterSpec(
-			nonce : byte[],
-			counter : int
-		);
-	}	
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DHGenParameterSpec.mark b/src/test/resources/Mark/bouncycastle/DHGenParameterSpec.mark
deleted file mode 100644
index b90a800b6764dd13e23d3dd464b323716aaaef8f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DHGenParameterSpec.mark
+++ /dev/null
@@ -1,15 +0,0 @@
-package java.jca
-
-entity DHGenParameterSpec {
-	
-	var primeSize;
-	var exponentSize;
-	
-	
-	op instantiate {
-		javax.crypto.spec.DHGenParameterSpec(
-			primeSize : int,
-			exponentSize : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DHParameterSpec.mark b/src/test/resources/Mark/bouncycastle/DHParameterSpec.mark
deleted file mode 100644
index 9d15c16fb3ed173287e3926a661b4a7246a2ca14..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DHParameterSpec.mark
+++ /dev/null
@@ -1,22 +0,0 @@
-package java.jca
-
-entity DHParameterSpec {
-	
-	var p;
-	var g;
-	var l;
-	
-	
-	op instantiate {
-		javax.crypto.spec.DHParameterSpec(
-			p : java.math.BigInteger,
-			g : java.math.BigInteger
-		);
-		javax.crypto.spec.DHParameterSpec(
-			p : java.math.BigInteger,
-			g : java.math.BigInteger,
-			l : int
-		);
-	}
-	
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DHPrivateKeySpec.mark b/src/test/resources/Mark/bouncycastle/DHPrivateKeySpec.mark
deleted file mode 100644
index d505965202184a4a54e96918873c58538f1f1945..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DHPrivateKeySpec.mark
+++ /dev/null
@@ -1,16 +0,0 @@
-package java.jca
-
-entity DHPrivateKeySpec {
-	
-	var x;
-	var p;
-	var g;
-	
-	op instantiate {
-		javax.crypto.spec.DHPrivateKeySpec(
-			x : java.math.BigInteger,
-			p : java.math.BigInteger,
-			g : java.math.BigInteger
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DHPublicKeySpec.mark b/src/test/resources/Mark/bouncycastle/DHPublicKeySpec.mark
deleted file mode 100644
index 4a563dd84cc6cea5c9b097c274e5b553a8c92f64..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DHPublicKeySpec.mark
+++ /dev/null
@@ -1,16 +0,0 @@
-package java.jca
-
-entity DHPublicKeySpec {
-	
-	var y;
-	var p;
-	var g;
-	
-	op instantiate {
-		javax.crypto.spec.DHPublicKeySpec(
-			y : java.math.BigInteger,
-			p : java.math.BigInteger,
-			g : java.math.BigInteger
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DSAGenParameterSpec.mark b/src/test/resources/Mark/bouncycastle/DSAGenParameterSpec.mark
deleted file mode 100644
index b4c3130a10ab3fe6794dcb61007dbb1b5ba3da9c..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DSAGenParameterSpec.mark
+++ /dev/null
@@ -1,22 +0,0 @@
-package java.jca
-
-entity DSAGenParameterSpec {
-    
-    var primePLen;
-    var subprimeQLen;
-    var seedLen;
-    
-    
-    op instantiate {
-        java.security.spec.DSAGenParameterSpec(
-            primePLen : int,
-            subprimeQLen : int
-        );
-        java.security.spec.DSAGenParameterSpec(
-            primePLen : int,
-            subprimeQLen : int,
-            seedLen : int
-        );
-    }
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DSAParameterSpec.mark b/src/test/resources/Mark/bouncycastle/DSAParameterSpec.mark
deleted file mode 100644
index 612f3bdc68261e907e263293a018a0edcf25a850..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DSAParameterSpec.mark
+++ /dev/null
@@ -1,17 +0,0 @@
-package java.jca
-
-entity DSAParameterSpec {
-    
-    var p;
-    var q;
-    var g;
-    
-    
-    op intialize {
-        java.security.spec.DSAParameterSpec(
-            p : java.math.BigInteger,
-            q : java.math.BigInteger,
-            g : java.math.BigInteger
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DSAPrivateKeySpec.mark b/src/test/resources/Mark/bouncycastle/DSAPrivateKeySpec.mark
deleted file mode 100644
index cd1a37f21db113aaf025bae313d520f6f31f32b0..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DSAPrivateKeySpec.mark
+++ /dev/null
@@ -1,18 +0,0 @@
-package java.jca
-
-entity DSAPrivateKeySpec {
-	
-	var x;
-	var p;
-	var q;
-	var g;
-	
-	op instantiate {
-	    java.security.spec.DSAPrivateKeySpec(
-	    	x : java.math.BigInteger,
-	    	p : java.math.BigInteger,
-	    	q : java.math.BigInteger,
-	    	g : java.math.BigInteger
-	    );
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/DSAPublicKeySpec.mark b/src/test/resources/Mark/bouncycastle/DSAPublicKeySpec.mark
deleted file mode 100644
index cfe67350abc06adab2488ccb18603f9e4b3c89af..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/DSAPublicKeySpec.mark
+++ /dev/null
@@ -1,18 +0,0 @@
-package java.jca
-
-entity DSAPublicKeySpec {
-	
-	var y;
-	var p;
-	var q;
-	var g;
-	
-	op instantiate {
-	    java.security.spec.DSAPublicKeySpec(
-	    	y : java.math.BigInteger,
-	    	p : java.math.BigInteger,
-	    	q : java.math.BigInteger,
-	    	g : java.math.BigInteger
-	    );
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECFieldF2m.mark b/src/test/resources/Mark/bouncycastle/ECFieldF2m.mark
deleted file mode 100644
index e069f482659f7ee1bc55c541f7393cd4cb12aa47..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECFieldF2m.mark
+++ /dev/null
@@ -1,22 +0,0 @@
-package java.jca
-
-entity ECFieldF2m {
-    
-    var m;
-    var ks;
-    var rp;
-    
-    op instantiate {
-        java.security.spec.ECFieldF2m(
-            m : int
-        );
-        java.security.spec.ECFieldF2m(
-            m : int,
-            ks : int[]
-        );
-        java.security.spec.ECFieldF2m(
-            m : int,
-            rp : java.math.BigInteger
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECFieldFp.mark b/src/test/resources/Mark/bouncycastle/ECFieldFp.mark
deleted file mode 100644
index 0199f1673a2b93f7d7af5c66852f49b60cf9f633..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECFieldFp.mark
+++ /dev/null
@@ -1,13 +0,0 @@
-package java.jca
-
-entity ECFieldFp {
-    
-    var p;
-    
-    
-    op instantiate {
-        java.security.spec.ECFieldFp(
-            p : java.math.BigInteger
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECGenParameterSpec.mark b/src/test/resources/Mark/bouncycastle/ECGenParameterSpec.mark
deleted file mode 100644
index c0e451e505881b448847533abd648e2283bea194..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECGenParameterSpec.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity ECGenParameterSpec {
-	
-	var stdName;
-	
-	
-	op instantiate {
-		java.security.spec.ECGenParameterSpec(
-			stdName : java.lang.String 
-		);
-	}
-	
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECKey.mark b/src/test/resources/Mark/bouncycastle/ECKey.mark
deleted file mode 100644
index e80cae5e3ce1883a061e72e145b8fff31138f5f2..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECKey.mark
+++ /dev/null
@@ -1,5 +0,0 @@
-package java.jca
-
-entity ECKey {
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECParameterSpec.mark b/src/test/resources/Mark/bouncycastle/ECParameterSpec.mark
deleted file mode 100644
index 3eb4049b29e58835c23a89c277bbce65ada93c87..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECParameterSpec.mark
+++ /dev/null
@@ -1,19 +0,0 @@
-package java.jca
-
-entity ECParameterSpec {
-
-	var curve;
-	var g;
-	var n;
-	var h;
-	
-
-	op instantiate {
-		java.security.spec.ECParameterSpec(
-			curve : java.security.spec.EllipticCurve, 
-			g : java.security.spec.ECPoint,
-			n : java.math.BigInteger,
-			h : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECPoint.mark b/src/test/resources/Mark/bouncycastle/ECPoint.mark
deleted file mode 100644
index fb9a58a29843a3b0504a462d88e34e0f6505efc1..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECPoint.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity ECPoint {
-	
-	var x;
-	var y;
-	
-	op instantiate {
-		java.security.spec.ECPoint(
-			x : java.math.BigInteger,
-			y : java.math.BigInteger
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECPrivateKeySpec.mark b/src/test/resources/Mark/bouncycastle/ECPrivateKeySpec.mark
deleted file mode 100644
index 1f91cca021f880b470953b25708847dab5b78e4f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECPrivateKeySpec.mark
+++ /dev/null
@@ -1,16 +0,0 @@
-package java.jca
-
-entity ECPrivateKeySpec {
-    
-    var s;
-    var params;
-    
-    
-    op instantiate {
-        java.security.spec.ECPrivateKeySpec(
-            s : java.math.BigInteger,
-            params : java.security.spec.ECParameterSpec
-        );
-    }
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/ECPublicKeySpec.mark b/src/test/resources/Mark/bouncycastle/ECPublicKeySpec.mark
deleted file mode 100644
index 17bf1a5960226f5f33f03c909fb8fc2a46028651..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/ECPublicKeySpec.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity ECPublicKeySpec {
-	
-	var w;
-	var params;
-	
-	op instantiate {
-		java.security.spec.ECPublicKeySpec(
-			w : java.security.spec.ECPoint,
-			params : java.security.spec.ECParameterSpec
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/EncodedKeySpec.mark b/src/test/resources/Mark/bouncycastle/EncodedKeySpec.mark
deleted file mode 100644
index eb5252760d7f0141dba94f7f87eceb42b5ec0270..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/EncodedKeySpec.mark
+++ /dev/null
@@ -1,18 +0,0 @@
-package java.jca
-
-entity EncodedKeySpec {
-	
-	var encodedKey;
-	var algorithm;
-	
-	
-	op instantiate {
-		java.security.spec.EncodedKeySpec(
-			encodedKey : byte[]
-		);
-		java.security.spec.EncodedKeySpec(
-			encodedKey : byte[],
-			algorithm : java.lang.String
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/GCMParameterSpec.mark b/src/test/resources/Mark/bouncycastle/GCMParameterSpec.mark
deleted file mode 100644
index ed3c7f27e9a6b259f48389cb40a7074a23c0861f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/GCMParameterSpec.mark
+++ /dev/null
@@ -1,22 +0,0 @@
-package java.jca
-
-entity GCMParameterSpec {
-	
-	var tLen;
-	var src;
-	var offset;
-	var len;
-	
-	op instantiate {
-		javax.crypto.spec.GCMParameterSpec(
-			tLen : int,
-			src : byte[]
-		);
-		javax.crypto.spec.GCMParameterSpec(
-			tLen : int,
-			src : byte[],
-			offset : int,
-			len : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/HMACParameterSpec.mark b/src/test/resources/Mark/bouncycastle/HMACParameterSpec.mark
deleted file mode 100644
index a243d3321ab1d4addaa6efef3e7b15e212bf0be3..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/HMACParameterSpec.mark
+++ /dev/null
@@ -1,12 +0,0 @@
-package java.jca
-
-entity HMACParameterSpec {
-	
-	var outputLength;
-	
-	op instantiate {
-		javax.xml.crypto.dsig.spec.HMACParameterSpec(
-			outputLength : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/IvParameterSpec.mark b/src/test/resources/Mark/bouncycastle/IvParameterSpec.mark
deleted file mode 100644
index 2ccc1a81bc59893241a87ab934f6f53b2965befa..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/IvParameterSpec.mark
+++ /dev/null
@@ -1,19 +0,0 @@
-package java.jca
-
-entity IvParameterSpec {
-	
-	var iv;
-	var offset;
-	var len;
-	
-	op instantiate {
-		javax.crypto.spec.IvParameterSpec(
-			iv : byte[]
-		);
-		javax.crypto.spec.IvParameterSpec(
-			iv : byte[],
-			offset : int,
-			len : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/KeyFactory.mark b/src/test/resources/Mark/bouncycastle/KeyFactory.mark
deleted file mode 100644
index 7e7bb8915aecd8827af60bca6c98422b4adfd741..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyFactory.mark
+++ /dev/null
@@ -1,31 +0,0 @@
-package java.jca
-
-entity KeyFactory {
-    
-    var algorithm;
-    var provider;
-    
-    var keyspec;
-    var prikey;
-    var pubkey;
-    
-    var inkey;
-    var outkey;
-    
-    op instantiate {
-        java.security.KeyFactory.getInstance(algorithm : java.lang.String);
-        java.security.KeyFactory.getInstance(
-            algorithm : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op generate {
-        prikey = java.security.KeyFactory.generatePrivate(keyspec : java.security.spec.KeySpec);
-        pubkey = java.security.KeyFactory.generatePublic(keyspec : java.security.spec.KeySpec);
-    }
-    
-    op translate {
-        outkey = java.security.KeyFactory.translateKey(inkey : java.security.Key);
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/KeyGenerator.mark b/src/test/resources/Mark/bouncycastle/KeyGenerator.mark
deleted file mode 100644
index 718e1a4c557e7dfe515a03ff0921980c972f9686..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyGenerator.mark
+++ /dev/null
@@ -1,40 +0,0 @@
-package java.jca
-
-entity KeyGenerator {
-    
-    var algorithm;
-    var provider;
-    
-    var keysize;
-    var random;
-    var params;
-    
-    var key;
-    
-    
-    op instantiate {
-        javax.crypto.KeyGenerator.getInstance(algorithm : java.lang.String);
-        javax.crypto.KeyGenerator.getInstance(
-            algorithm : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op init {
-        javax.crypto.KeyGenerator.init(keysize : int);
-        javax.crypto.KeyGenerator.init(
-            keysize : int,
-            random : java.security.SecureRandom
-        );
-        javax.crypto.KeyGenerator.init(random : java.security.SecureRandom);
-        javax.crypto.KeyGenerator.init(params : java.security.spec.AlgorithmParameterSpecs);
-        javax.crypto.KeyGenerator.init(
-            params : java.security.spec.AlgorithmParameterSpec,
-            random : java.security.SecureRandom
-        );
-    }
-    
-    op generate {
-        key = javax.crypto.KeyGenerator.generateKey();
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/KeyPair.mark b/src/test/resources/Mark/bouncycastle/KeyPair.mark
deleted file mode 100644
index 5067b130cc3c1705e859d9e2358fe2215e432719..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyPair.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity KeyPair {
-    
-    var publicKey;
-    var privateKey;
-    
-    op instantiate {
-        java.security.KeyPair(
-            publicKey : java.security.PublicKey,
-            privateKey : java.security.PrivateKey
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/KeyPairGenerator.mark b/src/test/resources/Mark/bouncycastle/KeyPairGenerator.mark
deleted file mode 100644
index b9f919f620413286e56ab6f8c173766104ef934f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyPairGenerator.mark
+++ /dev/null
@@ -1,46 +0,0 @@
-package java.jca
-
-entity KeyPairGenerator {
-    
-    var algorithm;
-    var provider;
-    
-    var keysize;
-    var random;
-    var params;
-    
-    var keypair;
-    
-    
-    op instantiate {
-        java.security.KeyPairGenerator.getInstance(
-            algorithm : java.lang.String
-        );
-        java.security.KeyPairGenerator.getInstance(
-            algorithm : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op initialize {
-        java.security.KeyPairGenerator.initialize(
-            keysize : int
-        );
-        java.security.KeyPairGenerator.initialize(
-            keysize : int,
-            random : java.security.SecureRandom
-        );
-        java.security.KeyPairGenerator.initialize(
-            params : java.security.spec.AlgorithmParameterSpec
-        );
-        java.security.KeyPairGenerator.initialize(
-            params : java.security.spec.AlgorithmParameterSpec,
-            random : java.security.SecureRandom
-        );
-    }
-    
-    op generate {
-        keypair = java.security.KeyPairGenerator.generateKeyPair();
-        keypair = java.security.KeyPairGenerator.genKeyPair();
-    }
-}
diff --git a/src/test/resources/Mark/bouncycastle/KeyStore.PasswordProtection.mark b/src/test/resources/Mark/bouncycastle/KeyStore.PasswordProtection.mark
deleted file mode 100644
index 64a91baeec5d6a8508e789a76a5a69ea267f993d..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyStore.PasswordProtection.mark
+++ /dev/null
@@ -1,19 +0,0 @@
-package java.jca
-
-entity KeyStore.PasswordProtection {
-    
-    var password;
-    var protectionAlgorithm;
-    var protectionParameters;
-    
-    op instantiate {
-        java.security.KeyStore.PasswordProtection(
-            password : char[]
-        );
-        java.security.KeyStore.PasswordProtection(
-            password : char[],
-            protectionAlgorithm : java.lang.String,
-            protectionParameters : java.security.spec.AlgorithmParameterSpec
-        );
-    }
-}
diff --git a/src/test/resources/Mark/bouncycastle/KeyStore.mark b/src/test/resources/Mark/bouncycastle/KeyStore.mark
deleted file mode 100644
index e92b2a1b66c3b326671c1c75c8d58b97eeb04225..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/KeyStore.mark
+++ /dev/null
@@ -1,60 +0,0 @@
-package java.jca
-
-entity KeyStore {
-    
-    var file;
-    var password;
-    var param;
-    var type;
-    var provider;
-    
-    var alias;
-    var cert;
-    var entry;
-    var protParam;
-    var rawKey;
-    var certChain;
-    var key;
-    
-    
-    op instantiate {
-        java.security.KeyStore.getInstance(
-            file : java.io.File,
-            password : char[]
-        );
-        java.security.KeyStore.getInstance(
-            file : java.io.File,
-            param : java.security.KeyStore.LoadStoreParameter
-        );
-        java.security.KeyStore.getInstance(
-            type : java.lang.String
-        );
-        java.security.KeyStore.getInstance(
-            type : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op store {
-        java.security.KeyStore.setCertificateEntry(
-            alias : java.lang.String,
-            cert : java.security.cert.Certificate
-        );
-        java.security.KeyStore.setEntry(
-            alias : java.lang.String,
-            entry : java.security.KeyStore.Entry,
-            protParam : java.security.KeyStore.ProtectionParameter
-        );
-        java.security.KeyStore.setKeyEntry(
-            alias : java.lang.String,
-            rawKey : byte[],
-            certChain : java.security.cert.Certificate[]
-        );
-        java.security.KeyStore.setKeyEntry(
-            alias : java.lang.String,
-            key : java.security.Key,
-            password : char[],
-            certChain : java.security.cert.Certificate[]
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/MGF1ParameterSpec.mark b/src/test/resources/Mark/bouncycastle/MGF1ParameterSpec.mark
deleted file mode 100644
index 208f2ae1cc46ada9a7f5f7bfcb080b05528054e9..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/MGF1ParameterSpec.mark
+++ /dev/null
@@ -1,12 +0,0 @@
-package java.jca
-
-entity MGF1ParameterSpec {
-	
-	var mdName;
-	
-	op instantiate {
-		java.security.spec.MGF1ParameterSpec(
-			mdName : java.lang.String
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/MessageDigest.mark b/src/test/resources/Mark/bouncycastle/MessageDigest.mark
deleted file mode 100644
index 1ce9bd729ada6ce8a7559222d485ed36a5d261d2..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/MessageDigest.mark
+++ /dev/null
@@ -1,39 +0,0 @@
-package java.jca
-
-/*
- * Represents java.security.MessageDigest
- */
-entity MessageDigest {
-    
-    var algorithm;
-    var provider;
-    var input;
-    var digest;
-    
-    op instantiate {
-        java.security.MessageDigest.getInstance(algorithm : java.lang.String);
-        java.security.MessageDigest.getInstance(
-            algorithm : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op update {
-        java.security.MessageDigest.update(input : byte | byte[] | java.nio.ByteBuffer);
-        java.security.MessageDigest.update(
-            input : byte[],
-            ...
-        );
-    }
-    
-    op digest {
-        digest = java.security.MessageDigest.digest();
-        digest = java.security.MessageDigest.digest(input : byte[]);
-        java.security.MessageDigest.digest(digest : byte[], ...);
-    }
-    
-    op reset {
-        java.security.MessageDigest.reset();
-    }
-    
-}
diff --git a/src/test/resources/Mark/bouncycastle/OAEPParameterSpec.mark b/src/test/resources/Mark/bouncycastle/OAEPParameterSpec.mark
deleted file mode 100644
index 4d9060a8c03c8cbca5eb5aba38e7b3eb02c75276..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/OAEPParameterSpec.mark
+++ /dev/null
@@ -1,19 +0,0 @@
-package java.jca
-
-entity OAEPParameterSpec {
-	
-	var mdName;
-	var mgfName;
-	var mgfSpec;
-	var pSrc;
-	
-	op instantiate {
-		javax.crypto.spec.OAEPParameterSpec(
-			mdName : java.lang.String,
-			mgfName : java.lang.String,
-			mgfSpec : java.security.spec.AlgorithmParameterSpec,
-			pSrc : javax.crypto.spec.PSource
-		);
-	}
-	
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PBEKey.mark b/src/test/resources/Mark/bouncycastle/PBEKey.mark
deleted file mode 100644
index b19111e4bc2696b809230227e946d0a78ed15b8a..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PBEKey.mark
+++ /dev/null
@@ -1,5 +0,0 @@
-package java.jca
-
-entity PBEKey {
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PBEKeySpec.mark b/src/test/resources/Mark/bouncycastle/PBEKeySpec.mark
deleted file mode 100644
index aa14dcbea0d012fe1e51f40a5c7f7b048bf002d2..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PBEKeySpec.mark
+++ /dev/null
@@ -1,26 +0,0 @@
-package java.jca
-
-entity PBEKeySpec {
-	
-	var password;
-	var salt;
-	var iterationCount;
-	var keyLength;
-	
-	op instantiate {
-		javax.crypto.spec.PBEKeySpec(
-			password : byte[]
-		);
-		javax.crypto.spec.PBEKeySpec(
-			password : byte[],
-			salt : byte[],
-			iterationCount : int
-		);
-		javax.crypto.spec.PBEKeySpec(
-			password : byte[],
-			salt : byte[],
-			iterationCount : int,
-			keyLength : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PBEParameterSpec.mark b/src/test/resources/Mark/bouncycastle/PBEParameterSpec.mark
deleted file mode 100644
index debd301db85b1c16bc781e391805dacf96808a6f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PBEParameterSpec.mark
+++ /dev/null
@@ -1,20 +0,0 @@
-package java.jca
-
-entity PBEParameterSpec {
-	
-	var salt;
-	var iterationCount;
-	var paramSpec;
-	
-	op instantiate {
-		javax.crypto.spec.PBEParameterSpec(
-			salt : byte[],
-			iterationCount : int
-		);
-		javax.crypto.spec.PBEParameterSpec(
-			salt : byte[],
-			iterationCount : int,
-			paramSpec : java.security.AlgorithmParameter
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PKCS8EncodedKeySpec.mark b/src/test/resources/Mark/bouncycastle/PKCS8EncodedKeySpec.mark
deleted file mode 100644
index bb19813800d66610ba63fcacde0e41964e23fe8f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PKCS8EncodedKeySpec.mark
+++ /dev/null
@@ -1,18 +0,0 @@
-package java.jca
-
-entity PKCS8EncodedKeySpec {
-	
-	var encodedKey;
-	var algorithm;
-	
-	op instantiate {
-		java.security.spec.PKCS8EncodedKeySpec(
-			encodedKey : byte[]
-		);
-		java.security.spec.PKCS8EncodedKeySpec(
-			encodedKey : byte[],
-			algorithm : java.lang.String
-		);
-	}
-	
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PSSParameterSpec.mark b/src/test/resources/Mark/bouncycastle/PSSParameterSpec.mark
deleted file mode 100644
index 5b7256eeeb1f7f2b00ffac4601600fbf1dae7536..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PSSParameterSpec.mark
+++ /dev/null
@@ -1,23 +0,0 @@
-package java.jca
-
-entity PSSParameterSpec {
-	
-	var saltLen;
-	var mdName;
-	var mgfName;
-	var mgfSpec;
-	var trailerField;
-	
-	op instantiate {
-		java.security.spec.PSSParameterSpec(
-			saltLen : int
-		);
-		java.security.spec.PSSParameterSpec(
-			mdName : java.lang.String,
-			mgfName : java.lang.String,
-			mgfSpec : java.security.spec.AlgorithmParameterSpec,
-			saltLen : int,
-			trailerField : int
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PrivateKey.mark b/src/test/resources/Mark/bouncycastle/PrivateKey.mark
deleted file mode 100644
index 48d7aa1f163d0968c7be49c05b47ca3b2d053a09..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PrivateKey.mark
+++ /dev/null
@@ -1,5 +0,0 @@
-package java.jca
-
-entity PrivateKey {
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/PublicKey.mark b/src/test/resources/Mark/bouncycastle/PublicKey.mark
deleted file mode 100644
index 44b8affffc1d5327879ce2e5d6d6957c2bd69150..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/PublicKey.mark
+++ /dev/null
@@ -1,5 +0,0 @@
-package java.jca
-
-entity PublicKey {
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RSAKeyGenParameterSpec.mark b/src/test/resources/Mark/bouncycastle/RSAKeyGenParameterSpec.mark
deleted file mode 100644
index b8b938ebc3ace4f7595aec7d86b5c44eedd4fd81..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RSAKeyGenParameterSpec.mark
+++ /dev/null
@@ -1,20 +0,0 @@
-package java.jca
-
-entity RSAKeyGenParameterSpec {
-	
-	var keySize;
-	var publicExponent;
-	var keyParams;
-	
-	op instantiate {
-		java.security.spec.RSAKeyGenParameterSpec(
-			keysize : int,
-			publicExponent : java.math.BigInteger
-		);
-		java.security.spec.RSAKeyGenParameterSpec(
-			keysize : int,
-			publicExponent : java.math.BigInteger,
-			keyParams : java.security.spec.AlgorithmParameterSpec
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RSAMultiPrimePrivateCrtKeySpec.mark b/src/test/resources/Mark/bouncycastle/RSAMultiPrimePrivateCrtKeySpec.mark
deleted file mode 100644
index 81c6d518051e5348a92ce8601d1e7fc583ea0d1c..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RSAMultiPrimePrivateCrtKeySpec.mark
+++ /dev/null
@@ -1,42 +0,0 @@
-package java.jca
-
-entity RSAMultiPrimePrivateCrtKeySpec {
-	
-	var modulus;
-	var publicExponent;
-	var privateExponent;
-	var primeP;
-	var primeQ;
-	var primeExponentP;
-	var primeExponentQ;
-	var crtCoefficient;
-	var otherPrimeInfo;
-	var keyParams;
-
-	
-	op instantiate {
-		java.security.spec.RSAMultiPrimePrivateCrtKeySpec(
-			modulus : java.math.BigInteger,
-			publicExponent : java.math.BigInteger,
-			privateExponent : java.math.BigInteger,
-			primeP : java.math.BigInteger,
-			primeQ : java.math.BigInteger,
-			primeExponentP : java.math.BigInteger,
-			primeExponentQ : java.math.BigInteger,
-			crtCoefficient : java.math.BigInteger,
-			otherPrimeInfo : java.security.spec.RSAOtherPrimeInfo[]
-		);
-		java.security.spec.RSAMultiPrimePrivateCrtKeySpec(
-			modulus : java.math.BigInteger,
-			publicExponent : java.math.BigInteger,
-			privateExponent : java.math.BigInteger,
-			primeP : java.math.BigInteger,
-			primeQ : java.math.BigInteger,
-			primeExponentP : java.math.BigInteger,
-			primeExponentQ : java.math.BigInteger,
-			crtCoefficient : java.math.BigInteger,
-			otherPrimeInfo : java.security.spec.RSAOtherPrimeInfo[],
-			keyParams : java.security.spec.AlgorithmParameterSpec
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RSAPrivateCrtKeySpec.mark b/src/test/resources/Mark/bouncycastle/RSAPrivateCrtKeySpec.mark
deleted file mode 100644
index 8c506c5ff6bb0f5f71a135717c1d5ffd7cee7c25..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RSAPrivateCrtKeySpec.mark
+++ /dev/null
@@ -1,38 +0,0 @@
-package java.jca
-
-entity RSAPrivateCrtKeySpec {
-	
-	var modulus;
-	var publicExponent;
-	var privateExponent;
-	var primeP;
-	var primeQ;
-	var primeExponentP;
-	var primeExponentQ;
-	var crtCoefficient;
-	var keyParams;
-	
-	op instantiate {
-		java.security.spec.RSAPrivateCrtKeySpec(
-			modulus : java.math.BigInteger,
-			publicExponent : java.math.BigInteger,
-			privateExponent : java.math.BigInteger,
-			primeP : java.math.BigInteger,
-			primeQ : java.math.BigInteger,
-			primeExponentP : java.math.BigInteger,
-			primeExponentQ : java.math.BigInteger,
-			crtCoefficient : java.math.BigInteger
-		);
-		java.security.spec.RSAPrivateCrtKeySpec(
-			modulus : java.math.BigInteger,
-			publicExponent : java.math.BigInteger,
-			privateExponent : java.math.BigInteger,
-			primeP : java.math.BigInteger,
-			primeQ : java.math.BigInteger,
-			primeExponentP : java.math.BigInteger,
-			primeExponentQ : java.math.BigInteger,
-			crtCoefficient : java.math.BigInteger,
-			keyParams : java.security.spec.AlgorithmParameterSpec
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RSAPrivateKeySpec.mark b/src/test/resources/Mark/bouncycastle/RSAPrivateKeySpec.mark
deleted file mode 100644
index 69a8732e9a8b1e285173f138a5838e8e5de2b6c6..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RSAPrivateKeySpec.mark
+++ /dev/null
@@ -1,21 +0,0 @@
-package java.jca
-
-entity RSAPrivateKeySpec {
-    
-    var modulus;
-    var privateExponent;
-    var params;
-    
-    
-    op instantiate {
-        java.security.spec.RSAPrivateKeySpec(
-            modulus : java.math.BigInteger,
-            privateExponent : java.math.BigInteger
-        );
-        java.security.spec.RSAPrivateKeySpec(
-            modulus : java.math.BigInteger,
-            privateExponent : java.math.BigInteger,
-            params : java.security.spec.AlgorithmParameterSpec
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RSAPublicKeySpec.mark b/src/test/resources/Mark/bouncycastle/RSAPublicKeySpec.mark
deleted file mode 100644
index 32f8764c560ad8a85287f5bc7af015674d157d1a..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RSAPublicKeySpec.mark
+++ /dev/null
@@ -1,21 +0,0 @@
-package java.jca
-
-entity RSAPublicKeySpec {
-    
-    var modulus;
-    var publicExponent;
-    var params;
-    
-    
-    op instantiate {
-        java.security.spec.RSAPublicKeySpec(
-            modulus : java.math.BigInteger,
-            publicExponent : java.math.BigInteger
-        );
-        java.security.spec.RSAPublicKeySpec(
-            modulus : java.math.BigInteger,
-            publicExponent : java.math.BigInteger,
-            params : java.security.spec.AlgorithmParameterSpec
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RulesBase_Cipher.mark b/src/test/resources/Mark/bouncycastle/RulesBase_Cipher.mark
deleted file mode 100644
index 84327ff3fc80e7ea3da98c7854763bdc3abd62c7..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesBase_Cipher.mark
+++ /dev/null
@@ -1,50 +0,0 @@
-package base.jca
-
-/**
- * 
- */
-rule Crypt {
-    using
-        Cipher as c
-    when
-        _split(c.transform, "/", 1) in ["CBC", "CTR"]
-        && (c.opmode == "Cipher.ENCRYPT_MODE"
-            || c.opmode == "Cipher.DECRYPT_MODE"
-            || c.opmode == "1" /* ENCRYPT_MODE */
-            || c.opmode == "2" /* DECRYPT_MODE */
-        )
-    ensure
-        order c.instantiate(),
-            c.init(),
-            c.update()*,
-            c.finalize()
-    onfail
-        InvalidOrderOfCipherOperations
-}
-
-/**
- * 
- */
-rule AEAD_Crypt {
-    using
-        Cipher as c
-    when
-        _split(c.transform, "/", 1) in ["CCM", "GCM"]
-        && (c.opmode == "Cipher.ENCRYPT_MODE"
-            || c.opmode == "Cipher.DECRYPT_MODE"
-            || c.opmode == "1" /* ENCRYPT_MODE */
-            || c.opmode == "2" /* DECRYPT_MODE */
-        )
-    ensure
-        order c.instantiate(),
-            c.init(),
-            c.aad()*, /* optional because only called if actually supplying AAD */
-            c.update()*,
-            c.finalize()
-    onfail
-        InvalidOrderforAEAD
-}
-
-
-//// TODO order rule for key wrap
-//// TODO order rule for key unwrapping
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_Cipher.mark b/src/test/resources/Mark/bouncycastle/RulesTR_Cipher.mark
deleted file mode 100644
index d371cb8fcf69d49a0610369064b9615fed1a036f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_Cipher.mark
+++ /dev/null
@@ -1,371 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1. Blockchiffren
- * - block ciphers
- */
-rule ID_2_01 {
-    using
-        Cipher as c
-    ensure
-        _split(c.transform, "/", 0) in ["AES"] /* BSI TR-02102-1, ID 2.01 */
-        || _split(c.transform, "/", 0) in ["RSA"]
-    onfail
-        Invalid_TR21021_Cipher
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.1. Betriebsarten
- * - block cipher modes
- */
-rule ID_2_1_01 {
-    using
-        Cipher as c
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-    ensure
-        /*  */
-        _split(c.transform, "/", 1) in ["CCM", "GCM", "CBC", "CTR"]
-    onfail
-        InvalidCipherModeforAESBlockCipher
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - CCM non-repeated IV during key period
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the dynamic behaviour of the program to check this rule.
- */
-//rule ID_2_1_2_1_01 {
-//    using
-//        Cipher as c
-//    when
-//        _split(c.transform, "/", 0) in ["AES"]
-//        && _split(c.transform, "/", 1) in ["CCM"]
-//    ensure
-//        false
-//    onfail
-//        InsufficientCCMIVRenewal
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - CCM minimum length of authentication tag
- * 
- * Note:
- * Bouncy Castle uses GCMParameterSpec for AEAD cipher initialization.
- */
-rule ID_2_1_2_1_02 {
-    using
-        Cipher as c,
-        GCMParameterSpec as gcmspec
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["CCM"]
-    ensure
-        _is(c.paramspec,gcmspec)
-        && gcmspec.tLen >= 64
-    onfail
-        InsufficientCCMTagLength
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - GCM non-repeated IV during key period
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the dynamic behaviour of the program to check this rule.
- */
-//rule ID_2_1_2_2_01 {
-//    using
-//        Cipher as c,
-//        GCMParameterSpec as gcm
-//    when
-//        _split(c.transform, "/", 0) in ["AES"]
-//        && _split(c.transform, "/", 1) in ["GCM"]
-//    ensure
-//        false
-//    onfail
-//        InvalidGCMIV
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - GCM nonce length for authentication tag
- */
-rule ID_2_1_2_2_02 {
-    using
-        Cipher as c,
-        GCMParameterSpec as gcm
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["GCM"]
-        && _is(c.paramspec, gcm)
-    ensure
-        (_has_value(gcm.src) && _length(gcm.src) == 12) /* in bytes */
-        || (_has_value(gcm.len) && gcm.len == 12)
-    onfail
-        InvalidGCMAuthenticationNonceLength
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - GCM minimum length of authentication tag
- */
-rule ID_2_1_2_2_03 {
-    using
-        Cipher as c,
-        GCMParameterSpec as gcm
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["GCM"]
-        && _is(c.paramspec, gcm)
-    ensure
-        gcm.tLen >= 96 /* apparently, there are fixed sizes 96, 104, 112, 120 and 128 */
-    onfail
-        InsufficientGCMTagLength
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - CBC unpredictable IV
- */
-rule ID_2_1_2_3_01 {
-    using
-        Cipher as c,
-        IvParameterSpec as ivspec,
-        SecureRandom as sr
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["CBC"]
-    ensure
-        // IMPROV not just random IV
-        _is(c.paramspec, ivspec)
-        && _is(ivspec.iv, sr.randomBytes)
-    onfail
-        InvalidCBCIV
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.2. Betriebsbedingungen
- * - CTR non-repeated counter during key period
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the dynamic behaviour of the program to check this rule.
- */
-//rule ID_2_1_2_4_01 {
-//    using
-//        Cipher as c
-//    when
-//        _split(c.transform, "/", 0) in ["AES"]
-//        && _split(c.transform, "/", 1) in ["CTR"]
-//    ensure
-//        false
-//    onfail
-//        InvalidCTRCounter
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.1.3. Paddingverfahren
- * - CBC padding
- */
-rule ID_2_1_3_01 {
-    using
-        Cipher as c
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["CBC"]
-    ensure
-        _split(c.transform, "/", 2) in [
-            "ISO7816-4Padding", // 1. ISO-Padding, siehe [57], padding method 2 und [73], Appendix A
-            "PKCS5Padding", "PKCS7Padding" // 2. Padding gemäß [87], Abschnitt 6.3
-        ]
-    onfail
-        InvalidCBCPadding
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.2. Stromchiffren
- * - Integrity protection (e.g MAC)
- * 
- * Note:
- * - included in rule ID_2_2_02
- */
-//rule ID_2_2_01 {
-//    using
-//        Cipher as c
-//    ensure
-//        true
-//    onfail
-//        InsufficientStreamCipherIntegrityProtection
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 2.2. Stromchiffren
- * - AES/CTR with MAC
- */
-rule ID_2_2_02 {
-    using
-        Cipher as c,
-        Mac as m
-    when
-        _split(c.transform, "/", 0) in ["AES"]
-        && _split(c.transform, "/", 1) in ["CTR"]
-    ensure
-        // IMPROV insufficient application of MAC; MAC over complete stream required
-        _is(c.output, m.input)
-    onfail
-        InsufficientAESCTRIntegrityProtection
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.3. ECIES-Verschlüsselungsverfahren
- * - ECIES order of decryption operations
- * 
- * Note:
- * - implementation not provided by Bouncy Castle. Implementation by user likely to be error prone and should not be encouraged.
- */
-//rule ID_3_3_01 {
-//    using
-//        Cipher as c
-//    when
-//        false
-//    ensure
-//        false
-//    onfail
-//        InvalidECIESOperationOrder
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.3. ECIES-Verschlüsselungsverfahren
- * - ECIES curve parameters
- * 
- * Note:
- * - implementation not provided by Bouncy Castle. Implementation by user likely to be error prone and should not be encouraged.
- */
-//rule ID_3_3_02 {
-//    using
-//        Cipher as c
-//    when
-//        false
-//    ensure
-//        false
-//    onfail
-//        InvalidECIESCurveParameter
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.3. ECIES-Verschlüsselungsverfahren
- * - ECIES key derivation
- * 
- * Note:
- * - implementation not provided by Bouncy Castle. Implementation by user likely to be error prone and should not be encouraged.
- */
-//rule ID_3_3_03 {
-//    using
-//        Cipher as c
-//    when
-//        false
-//    ensure
-//        false
-//    onfail
-//        InvalidECIESSymmetricKeyDerivation
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.3. ECIES-Verschlüsselungsverfahren
- * - ECIES order of EC base point 
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the supplied order of an EC base point
- */
-//rule ID_3_3_04 {
-//    using
-//        Cipher as c
-//    when
-//        false
-//    ensure
-//        false
-//    onfail
-//        InsufficientECIESBasePointOrder
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.4. DLIES-Verschlüsselungsverfahren
- * 
- * Note:
- * - implementation not provided by Bouncy Castle. Implementation by user likely to be error prone and should not be encouraged.
- * - applies to requirements ID 3.4.01, ID 3.4.02 and ID 3.4.03
- */
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.5. RSA
- * - RSA EME-OAEP formatting scheme
- */
-rule ID_3_5_01 {
-    using
-        Cipher as c
-    when
-        _split(c.transform, "/", 0) == "RSA"
-        //&& (
-        //    c.opmode == "javax.crypto.Cipher.ENCRYPT_MODE"
-        //    || c.opmode == "javax.crypto.Cipher.DECRYPT_MODE"
-        //    || c.opmode == "1"
-        //    || c.opmode == "2"
-        //    )
-    ensure
-        _split(c.transform, "/", 2) in [
-                // "OAEPWITHSHA1ANDMGF1PADDING", "OAEPWITHSHA-1ANDMGF1PADDING" // recommended by referenced RFC 8017, but not a recommended hash function by BSI
-                // "OAEPWITHSHA224ANDMGF1PADDING", "OAEPWITHSHA-224ANDMGF1PADDING", // recommended by referenced RFC 8017, but not a recommended hash function by BSI
-                "OAEPWITHSHA256ANDMGF1PADDING", "OAEPWITHSHA-256ANDMGF1PADDING",
-                "OAEPWITHSHA384ANDMGF1PADDING", "OAEPWITHSHA-384ANDMGF1PADDING",
-                "OAEPWITHSHA512ANDMGF1PADDING", "OAEPWITHSHA-512ANDMGF1PADDING"//,
-                // "OAEPWITHSHA3-256ANDMGF1PADDING", // not listed by referenced RFC 8017, but recommended hash function by BSI
-                // "OAEPWITHSHA3-384ANDMGF1PADDING", // not listed by referenced RFC 8017, but recommended hash function by BSI
-                // "OAEPWITHSHA3-512ANDMGF1PADDING" // not listed by referenced RFC 8017, but recommended hash function by BSI
-            ]
-    onfail
-        InvalidRSAPadding
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 3.5. RSA
- * - RSA minimum length of modulus
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the supplied RSA public key.
- */
-//rule ID_3_5_02 {
-//    using
-//        Cipher as c,
-//        RSAPublicKeySpec as pubKey
-//    when
-//        _split(c.transform, "/", 0) == "RSA"
-//        && c.opmode == "javax.crypto.Cipher.ENCRYPT_MODE"
-//    ensure
-//        false
-//    onfail
-//        InsufficientRSAKeylength // FIXME valid until 2022
-//}
-
-/*
- * Check RSA key length on key generation.
- * 
- * Ensures that generated keys have sufficient length in compliance with BSI TR-02102-1 (Version 2019-01).
- * 
- */
-rule ID_3_5_02_RSAKeyGenParameterSpec {
-    using
-        RSAKeyGenParameterSpec as rsaKeyGenSpec
-    ensure
-        rsaKeyGenSpec.keysize >= 2000
-    onfail
-        InsufficientRSAKeylength // FIXME valid until 2022
-}
-
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_InstanceAuthentication.txt b/src/test/resources/Mark/bouncycastle/RulesTR_InstanceAuthentication.txt
deleted file mode 100644
index 2fc11a00212bea1e425f1079b4e03768412ffa55..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_InstanceAuthentication.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 6. Instanzauthentisierung
- * 
- * Note:
- * - requirements ID 6.1.01 and ID 6.1.02 already covered by rules for Cipher and MAC
- */
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_KeyAgreement.txt b/src/test/resources/Mark/bouncycastle/RulesTR_KeyAgreement.txt
deleted file mode 100644
index a5ea628a784a4d87081d5b85cf799b310cf3a0f4..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_KeyAgreement.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 7. Schlüsseleinigungsverfahren, Schlüsseltransportverfahren und Key-Update
- * 
- * Note:
- * - requirements ID 7.1.1.01, ID 7.1.1.02 and ID 7.2.1.01 already covered by rules for Cipher and MAC
- * - implementation for requirement ID 7.1.2.01 not provided by Bouncy Castle. Implementation by user likely to be error prone and should not be encouraged.
- * - unable to check requirements ID 7.2.2.1.01 and ID 7.2.2.2.01. We cannot sufficiently reason about supplied key length or parameters
- */
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_MAC.mark b/src/test/resources/Mark/bouncycastle/RulesTR_MAC.mark
deleted file mode 100644
index c30e1ce91b84d8fe19efdecc741effa41117f09d..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_MAC.mark
+++ /dev/null
@@ -1,187 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - MAC algorithms
- */
-rule ID_5_3_01 {
-    using
-        Mac as m
-    ensure
-        m.algorithm in [
-            "AESCMAC", // CMACs
-            "HMACSHA256", "HMACSHA512/256", "HMACSHA384", "HMACSHA512", "HMACSHA3-256", "HMACSHA3-384", "HMACSHA3-512", // HMACs
-            "AES-GMAC" // GMACs
-        ]
-    onfail
-        InvalidMACAlgorithm
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - CMAC minimum key length
- */
-rule ID_5_3_02_CMAC_Keygen {
-    using
-        Mac as m,
-        KeyGenerator as kg
-    when
-        m.algorithm in ["AESCMAC"]
-        && _is(m.key, kg.key)
-    ensure
-        // find a keygenerator of sufficient size
-        _is(m.key, kg.key)
-        && kg.keysize >= 128
-    onfail
-        InsufficientCMACKeyLength
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - CMAC minimum key length
- */
-rule ID_5_3_02_CMAC_HMAC_SecretKeyFactory {
-    using
-        Mac as m,
-        SecretKeySpec as sks,
-        SecretKeyFactory as kf
-    when
-        m.algorithm in ["AESCMAC"]
-        && _is(m.key, kf.outkey)
-    ensure
-        // find a keygenerator of sufficient size
-        _is(m.key, kf.outkey)
-        && _is(kf.keyspec, sks)
-        && (
-            (_has_value(sks.len) && sks.len >= 128)
-            || (!(_has_value(sks.len)) && _has_value(sks.key) && _length(sks.key) >= 16)
-        )
-    onfail
-        InsufficientCMACKeyLength
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - HMAC minimum key length
- */
-rule ID_5_3_02_HMAC_Keygen {
-    using
-        Mac as m,
-        KeyGenerator as kg
-    when
-        m.algorithm in ["HMACSHA256", "HMACSHA512/256", "HMACSHA384", "HMACSHA512", "HMACSHA3-256", "HMACSHA3-384", "HMACSHA3-512"]
-        && _is(m.key, kg.key)
-    ensure
-        // find a keygenerator of sufficient size
-        (
-            _is(m.key, kg.key)
-            && kg.keysize >= 128
-        )
-        || (
-             _is(m.key, kg.key)
-             && kg.algorithm == m.algorithm
-        )
-    onfail
-        InsufficientHMACKeyLength
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - HMAC minimum key length
- */
-rule ID_5_3_02_HMAC_SecretKeyFactory {
-    using
-        Mac as m,
-        SecretKeySpec as sks,
-        SecretKeyFactory as kf
-    when
-        m.algorithm in ["HMACSHA256", "HMACSHA512/256", "HMACSHA384", "HMACSHA512", "HMACSHA3-256", "HMACSHA3-384", "HMACSHA3-512"]
-        && _is(m.key, kf.outkey)
-        && _is(kf.keyspec, sks)
-    ensure
-        (
-            _is(m.key, kf.outkey)
-            && _is(kf.keyspec, sks)
-            && (
-                (_has_value(sks.len) && sks.len >= 128)
-                || (!(_has_value(sks.len)) && _has_value(sks.key) && _length(sks.key) >= 16)
-            )
-        )
-    onfail
-        InsufficientHMACKeyLength
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - GMAC minimum key length
- */
-rule ID_5_3_02_GMAC {
-    using
-        Mac as m,
-        KeyGenerator as kg,
-        SecretKeySpec as sks,
-        SecretKeyFactory as kf
-    when
-        m.algorithm in ["AES-GMAC"] && ( _is(m.key, kg.key)
-        || ( _is(m.key, kf.outkey) && _is(kf.keyspec, sks) ) )
-    ensure
-        // find a keygenerator of sufficient size
-        kg.keysize >= 128
-        || (_has_value(sks.len) && sks.len >= 128)
-        || (!(_has_value(sks.len)) && _has_value(sks.key) && _length(sks.key) >= 16)
-    onfail
-        InsufficientGMACKeyLength
-}
-
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - MAC minimum length of authentication tag
- */
-rule ID_5_3_03_CMAC {
-    using
-        Mac as m
-    when
-        m.algorithm in ["AESCMAC"]
-    ensure
-        // TODO check in future releases of Bouncy Castle
-        true // Bouncy Castle implementation uses block size of AES in bits (128) by default
-    onfail
-        InsufficientCMACTagLength
-}
-
-/**
-* BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - HMAC minimum length of authentication tag
- */
-rule ID_5_3_03_HMAC {
-    using
-        Mac as m,
-        HMACParameterSpec as spec
-    when
-        m.algorithm in ["HMACSHA256", "HMACSHA512/256", "HMACSHA384", "HMACSHA512", "HMACSHA3-256", "HMACSHA3-384", "HMACSHA3-512"]
-        && _is(m.params, spec)
-    ensure
-        _is(m.params, spec)
-        && spec.outputLength >= 96
-    onfail
-        InsufficientHMACTagLength
-}
-
-/**
-* BSI TR-02102-1 (Version 2019-01), 5.3. Message Authentication Code (MAC)
- * - GMAC minimum length of authentication tag
- */
-rule ID_5_3_03_GMAC {
-    using
-        Mac as m
-    when
-        m.algorithm in ["AES-GMAC"]
-    ensure
-        // TODO check in future releases of Bouncy Castle
-        true // Bouncy Castle uses 128 bits by default
-    onfail
-        InsufficientGMACTagLength
-}
-
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_MessageDigest.mark b/src/test/resources/Mark/bouncycastle/RulesTR_MessageDigest.mark
deleted file mode 100644
index 86d407b58807b78cf02b5ca49fe7284e63ee669f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_MessageDigest.mark
+++ /dev/null
@@ -1,17 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 4. Hashfunktionen
- * - hash functions
- */
-rule ID_4_01 {
-    using
-        MessageDigest as md
-    ensure
-        md.algorithm in [
-            "SHA-256", "SHA-512/256", "SHA-384", "SHA-512", // SHA-2
-            "SHA3-256", "SHA3-384", "SHA3-512" // SHA-3
-        ]
-    onfail
-        InvalidHashFunction
-}
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_PRNG.txt b/src/test/resources/Mark/bouncycastle/RulesTR_PRNG.txt
deleted file mode 100644
index bd975b50740cd3213c58e9730d2b216c87dc7de8..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_PRNG.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 9. Zufallszahlengeneratoren
- * 
- * Note:
- * - requirement ID 9.2.1.01 fulfiled by Bouncy Castle implementation
- * - requirement ID 9.2.2.01 does not apply to Bouncy Castle
- * - requirement ID 9.5.1.01 and ID 9.5.2.1 not checked because it is internal to the implementation of Bouncy Castle
- */
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/RulesTR_Signature.mark b/src/test/resources/Mark/bouncycastle/RulesTR_Signature.mark
deleted file mode 100644
index 3de17561a7ad052843d09364f1e3b80fb70b2fa7..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/RulesTR_Signature.mark
+++ /dev/null
@@ -1,81 +0,0 @@
-package rules.bsi.tr_02102_1.v2019_01
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.4.1. RSA und 5.4.2. Digital Signature Algorithm (DSA) und 5.4.3 DSA-Varianten basierend auf elliptischen Kurven
- * - signature algorithms
- * 
- * Note:
- * - includes requirements ID 5.4.1.01 and ID 5.4.3.01
- */
-rule ID_5_4 {
-    using 
-        Signature as s
-    ensure 
-        s.algorithm in [
-            "SHA256WITHRSAANDMGF1", "SHA512(256)WITHRSAANDMGF1", "SHA384WITHRSAANDMGF1", "SHA512WITHRSAANDMGF1", "SHA3-256WITHRSAANDMGF1", "SHA3-384WITHRSAANDMGF1", "SHA3-512WITHRSAANDMGF1", // RSA, EMSA-PSS
-            "SHA256WITHRSA/ISO9796-2", "SHA512(256)WITHRSA/ISO9796-2", "SHA384WITHRSA/ISO9796-2", "SHA512WITHRSA/ISO9796-2", // RSA, Digital Signature Scheme (DS) 2 und 3
-            "SHA256WITHDSA", "SHA384WITHDSA", "SHA512WITHDSA", "SHA3-256WITHDSA", "SHA3-384WITHDSA", "SHA3-512WITHDSA", // DSA
-            "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA", "SHA3-256WITHECDSA", "SHA3-384WITHECDSA", "SHA3-512WITHECDSA" // ECDSA
-        ]
-    onfail
-        InvalidSignatureAlgorithm
-}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.4.1. RSA
- * - RSA modulus length of key in bits
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the supplied RSA private key
- */
-//rule ID_5_4_1_02 {
-//    using 
-//        Signature as s,
-//        KeyFactory as kf,
-//        RSAPrivateKeySpec as rsaprivkey
-//    ensure 
-//        // TODO valid until 2022
-//        _is(s.privateKey, kf.prikey)
-//        && _is(kf.keyspec, rsaprivkey)
-//        && _bit_length(rsaprivkey.modulus) >= 2000
-//    onfail
-//        InsufficientRSAKeyLength
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.4.2. Digital Signature Algorithm (DSA)
- * - DSA modulus length of key in bits
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the supplied DSA private key
- */
-//rule ID_5_4_2_01 {
-//    using 
-//        Signature as s,
-//        KeyFactory as kf,
-//        DSAPrivateKeySpec as dsaprivkey
-//    ensure 
-//        // TODO valid until 2022
-//        _is(s.privateKey, kf.prikey)
-//        && _is(kf.keyspec, dsaprivkey)
-//        && _bit_length(dsaprivkey.modulus) >= 2000
-//    onfail
-//        InsufficientDSAKeyLength
-//}
-
-/**
- * BSI TR-02102-1 (Version 2019-01), 5.4.3. DSA-Varianten basierend auf elliptischen Kurven
- * - EC[K/G]DSA size of order q
- * 
- * Note:
- * - seems to be not checkable. We cannot sufficiently reason about the supplied EC private key
- */
-//rule ID_5_4_3_02 {
-//    using 
-//        Signature as s
-//    ensure 
-//        // TODO valid until 2022
-//        false
-//    onfail
-//        InsufficientECOrderQSize
-//}
diff --git a/src/test/resources/Mark/bouncycastle/Rules_BouncyCastleJCA.mark b/src/test/resources/Mark/bouncycastle/Rules_BouncyCastleJCA.mark
deleted file mode 100644
index 607197c8480e716d3d9ebd5107313dc30f76ed04..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/Rules_BouncyCastleJCA.mark
+++ /dev/null
@@ -1,223 +0,0 @@
-package java.bcjca
-
-rule BouncyCastleProvider_AlgorithmParameterGenerator {
-    using
-        AlgorithmParameterGenerator as apg
-    ensure 
-        _has_value(apg.provider)
-        && (
-            apg.provider == "BC"
-            || _is_instance(apg.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_AlgorithmParameterGenerator
-}
-
-rule BouncyCastleProvider_AlgorithmParameters {
-    using
-        AlgorithmParameters as ap
-    ensure 
-        _has_value(ap.provider)
-        && (
-            ap.provider == "BC"
-            || _is_instance(ap.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_AlgorithmParameters
-}
-
-rule BouncyCastleProvider_CertificateFactory {
-    using
-        CertificateFactory as cf
-    ensure 
-        _has_value(cf.provider)
-        && (
-            cf.provider == "BC"
-            || _is_instance(cf.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_CertificateFactory
-}
-
-rule BouncyCastleProvider_CertPathBuilder {
-    using
-        CertPathBuilder as cpb
-    ensure 
-        _has_value(cpb.provider)
-        && (
-            cpb.provider == "BC"
-            || _is_instance(cpb.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_CertPathBuilder
-}
-
-rule BouncyCastleProvider_CertPathValidator {
-    using
-        CertPathValidator as cpv
-    ensure 
-        _has_value(cpv.provider)
-        && (
-            cpv.provider == "BC"
-            || _is_instance(cpv.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_CertPathValidator
-}
-
-rule BouncyCastleProvider_CertStore {
-    using
-        CertStore as cs
-    ensure 
-        _has_value(cs.provider)
-        && (
-            cs.provider == "BC"
-            || _is_instance(cs.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_CertStore
-}
-
-rule BouncyCastleProvider_Cipher {
-    using
-        Cipher as c
-    ensure 
-        _has_value(c.provider)
-        && (
-            c.provider == "BC"
-            || _is_instance(c.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_Cipher
-}
-
-rule BouncyCastleProvider_KeyAgreement {
-    using
-        KeyAgreement as ka
-    ensure 
-        _has_value(ka.provider)
-        && (
-            ka.provider == "BC"
-            || _is_instance(ka.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_KeyAgreement
-}
-
-
-rule BouncyCastleProvider_KeyFactory {
-    using
-        KeyFactory as kf
-    ensure 
-        _has_value(kf.provider)
-        && (
-            kf.provider == "BC"
-            || _is_instance(kf.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_KeyFactory
-}
-
-rule BouncyCastleProvider_KeyGenerator {
-    using
-        KeyGenerator as kg
-    ensure 
-        _has_value(kg.provider)
-        && (
-            kg.provider == "BC"
-            || _is_instance(kg.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_KeyGenerator
-}
-
-rule BouncyCastleProvider_KeyPairGenerator {
-    using
-        KeyPairGenerator as kpg
-    ensure 
-        _has_value(kpg.provider)
-        && (
-            kpg.provider == "BC"
-            || _is_instance(kpg.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_KeyPairGenerator
-}
-
-rule BouncyCastleProvider_KeyStore {
-    using
-        KeyStore as ks
-    ensure 
-        _has_value(ks.provider)
-        && (
-            ks.provider == "BC"
-            || _is_instance(ks.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_KeyStore
-}
-
-rule BouncyCastleProvider_Mac {
-    using
-        Mac as m
-    ensure 
-        _has_value(m.provider)
-        && (
-            m.provider == "BC"
-            || _is_instance(m.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_Mac
-}
-
-rule BouncyCastleProvider_MessageDigest {
-    using
-        MessageDigest as md
-    ensure 
-        _has_value(md.provider)
-        && (
-            md.provider == "BC"
-            || _is_instance(md.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_MessageDigest
-}
-
-rule BouncyCastleProvider_SecretKeyFactory {
-    using
-        SecretKeyFactory as skf
-    ensure 
-        _has_value(skf.provider)
-        && (
-            skf.provider == "BC"
-            || _is_instance(skf.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_SecretKeyFactory
-}
-
-rule BouncyCastleProvider_SecureRandom {
-    using
-        SecureRandom as sr
-    ensure 
-        _has_value(sr.provider)
-        && (
-            sr.provider == "BC"
-            || _is_instance(sr.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_SecureRandom
-}
-
-rule BouncyCastleProvider_Signature {
-    using
-        Signature as s
-    ensure 
-        _has_value(s.provider)
-        && (
-            s.provider == "BC"
-            || _is_instance(s.provider, "org.bouncycastle.jce.provider.BouncyCastleProvider")
-        )
-    onfail
-        InvalidProvider_Signature
-}
diff --git a/src/test/resources/Mark/bouncycastle/SecretKey.mark b/src/test/resources/Mark/bouncycastle/SecretKey.mark
deleted file mode 100644
index 2a42d0459a9b1cb5762b02a93b266b37e8a1e46f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/SecretKey.mark
+++ /dev/null
@@ -1,5 +0,0 @@
-package java.jca
-
-entity SecretKey {
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/SecretKeyFactory.mark b/src/test/resources/Mark/bouncycastle/SecretKeyFactory.mark
deleted file mode 100644
index 8703ea34004570688a3f5d68d9872711df3b898f..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/SecretKeyFactory.mark
+++ /dev/null
@@ -1,35 +0,0 @@
-package java.jca
-
-entity SecretKeyFactory {
-    
-    var algorithm;
-    var provider;
-    
-    var keyspec;
-    
-    var inkey;
-    var outkey;
-    
-    
-    op instantiate {
-        javax.crypto.SecretKeyFactory.getInstance(
-            algorithm : java.lang.String
-        );
-        javax.crypto.SecretKeyFactory.getInstance(
-            algorithm : java.lang.String,
-            provider : java.lang.String | java.security.Provider
-        );
-    }
-    
-    op generate {
-        outkey = javax.crypto.SecretKeyFactory.generateSecret(
-            keyspec: java.security.spec.KeySpec
-        );
-    }
-    
-    op translate {
-        outkey = javax.crypto.SecretKeyFactory.translateKey(
-            inkey : javax.crypto.SecretKey
-        );
-    }
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/SecretKeySpec.mark b/src/test/resources/Mark/bouncycastle/SecretKeySpec.mark
deleted file mode 100644
index fbcb922741fc90bd040dde4205fe18f3928e7104..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/SecretKeySpec.mark
+++ /dev/null
@@ -1,23 +0,0 @@
-package java.jca
-
-entity SecretKeySpec {
-    
-    var key;
-    var offset;
-    var len;
-    var algorithm;
-    
-    op instantiate {
-        javax.crypto.spec.SecretKeySpec(
-            key : byte[],
-            offset : int,
-            len : int,
-            algorithm : java.lang.String
-        );
-        javax.crypto.spec.SecretKeySpec(
-            key : byte[],
-            algorithm : java.lang.String
-        );
-    }
-    
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/Signature.mark b/src/test/resources/Mark/bouncycastle/Signature.mark
deleted file mode 100644
index effa3213ef5746d2917ad91d422a6a419231bc45..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/Signature.mark
+++ /dev/null
@@ -1,89 +0,0 @@
-package java.jca
-
-entity Signature {
-	
-	var algorithm;
-	var provider;
-	
-	var privateKey;
-	var random;
-	
-	var certificate;
-	var publicKey;
-	
-	var b;
-	var data;
-	var off;
-	var len;
-	
-	var outbuf;
-	var offset;
-	var len;
-	
-	var signature;
-	var offset;
-	var length;
-	
-	op instantiate {
-		java.security.Signature.getInstance(
-			algorithm : java.lang.String
-		);
-		java.security.Signature.getInstance(
-			algorithm : java.lang.String,
-			provider : java.lang.String | java.security.Provider
-		);
-	}
-	
-	op initsign {
-		java.security.Signature.initSign(
-			privateKey : java.security.PrivateKey
-		);
-		java.security.Signature.initSign(
-			privateKey : java.security.PrivateKey,
-			random : java.security.SecureRandom
-		);
-	}
-	
-	op initverify {
-		java.security.Signature.initVerify(
-			certificate : java.security.cert.Certificate
-		);
-		java.security.Signature.initVerify(
-			publicKey : java.security.PublicKey
-		);
-	}
-	
-	op update {
-		java.security.Signature.update(
-			b : byte
-		);
-		java.security.Signature.update(
-			data : byte[] | java.nio.ByteBuffer
-		);
-		java.security.Signature.update(
-			data : byte[],
-			off : int,
-			len : int
-		);
-	}
-	
-	op sign {
-		signature = java.security.Signature.sign();
-		java.security.Signature.sign(
-			outbuf : byte[],
-			offseet : int,
-			len : int
-		);
-	}
-	
-	op verify {
-		java.security.Signature.verify(
-			signature : byte[]
-		);
-		java.security.Signature.verify(
-			signature : byte[],
-			offset : int,
-			length : int
-		);
-	}
-}
diff --git a/src/test/resources/Mark/bouncycastle/X509EncodedKeySpec.mark b/src/test/resources/Mark/bouncycastle/X509EncodedKeySpec.mark
deleted file mode 100644
index 298ef704a2f45f1082c6fe848a3e603c230f9f42..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/X509EncodedKeySpec.mark
+++ /dev/null
@@ -1,18 +0,0 @@
-package java.jca
-
-entity X509EncodedKeySpec {
-	
-	var encodedKey;
-	var algorithm;
-	
-	
-	op instantiate {
-		java.security.spec.X509EncodedKeySpec(
-			encodedKey : byte[]
-		);
-		java.security.spec.X509EncodedKeySpec(
-			encodedKey : byte[],
-			algorithm : java.lang.String
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/XECPrivateKeySpec.mark b/src/test/resources/Mark/bouncycastle/XECPrivateKeySpec.mark
deleted file mode 100644
index d093427f480e329ef8a3154cf45082c8412b9d81..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/XECPrivateKeySpec.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity XECPrivateKeySpec {
-	
-	var params;
-	var scalar;
-	
-	op instantiate {
-		java.security.spec.XECPrivateKeySpec(
-			params : java.security.spec.AlgorithmParameterSpec,
-			scalar : byte[]
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/XECPublicKeySpec.mark b/src/test/resources/Mark/bouncycastle/XECPublicKeySpec.mark
deleted file mode 100644
index e228310529ff6c728e4059ed8d90c6f121af52c2..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/bouncycastle/XECPublicKeySpec.mark
+++ /dev/null
@@ -1,14 +0,0 @@
-package java.jca
-
-entity XECPublicKeySpec {
-	
-	var params;
-	var u;
-	
-	op instantiate {
-		java.security.spec.XECPublicKeySpec(
-			params : java.security.spec.AlgorithmParameterSpec,
-			u : java.math.BigInteger
-		);
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/Mark/exampleMapping.yaml b/src/test/resources/Mark/exampleMapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..7358b83b3039400efe321af4df7de77d4aabbb2b
--- /dev/null
+++ b/src/test/resources/Mark/exampleMapping.yaml
@@ -0,0 +1,20 @@
+metrics:
+  - name: "TestMetric1"
+    rules:
+      - "WrongUseOfBotan_CipherMode"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
+  - name: "TestMetric2"
+    rules:
+      - "VariableNotInitialized"
+    configuration:
+        default: false
+        operator: "="
+        type: BOOLEAN
+        target:
+          - "true"
\ No newline at end of file
diff --git a/src/test/resources/Mark/jackson/ObjectMapper.mark b/src/test/resources/Mark/jackson/ObjectMapper.mark
deleted file mode 100644
index dad5edebbd8b5158873e3e9fdc83ddb465cd1c82..0000000000000000000000000000000000000000
--- a/src/test/resources/Mark/jackson/ObjectMapper.mark
+++ /dev/null
@@ -1,12 +0,0 @@
-entity ObjectMapper {
-	op instantiate {
-		com.fasterxml.jackson.databind.ObjectMapper();
-	}
-
-    /**
-    * Default typing has lead to various problems with Jackson in the past and should be avoided.
-    */
-	op enableDefaultTyping {
-	    forbidden com.fasterxml.jackson.databind.ObjectMapper.enableDefaultTyping();
-	}
-}
\ No newline at end of file
diff --git a/src/test/resources/codyze.yaml b/src/test/resources/codyze.yaml
index 66a79acffe705fa71b3e2fa3db5e45b11fdf78da..2a2abb4e81349df1376175c6f2889ae83b42518b 100644
--- a/src/test/resources/codyze.yaml
+++ b/src/test/resources/codyze.yaml
@@ -7,12 +7,16 @@ orchestrator:
     username: "clouditor"
     password: "clouditor"
 
-source: "src/test/resources/exampleFiles/TLSServer/"
+id: "default"
+source:
+  - exampleFiles/2_1_2_1_02.cpp
 output: "codyze.sarif"
-sarif: true
+rules: mark/medina.yaml
+
+mark:
+  builtin:
+    - bc-jsse/
 
 codyze:
-  mark:
-    - "src/test/resources/mark/demo/"
   no-good-findings: false
   pedantic: false
diff --git a/src/test/resources/demos/bcjsse/TlsServer.java b/src/test/resources/demos/bcjsse/TlsServer.java
deleted file mode 100644
index f71cad44e280da432554b7bb2df9d4be522558da..0000000000000000000000000000000000000000
--- a/src/test/resources/demos/bcjsse/TlsServer.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package de.fraunhofer.aisec.codyze.medina.demo.jsse;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
-
-import javax.net.ssl.*;
-import java.io.*;
-import java.net.Socket;
-import java.nio.file.Paths;
-import java.security.*;
-import java.security.cert.CertificateException;
-import java.util.Arrays;
-
-public class TlsServer {
-
-    private static final boolean DEBUG = true;
-
-    private SSLServerSocket socket;
-
-    private void configure(int port, String keystore, String keystorePwd) throws IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchProviderException {
-        // overview of functionality provided by BCJSSE
-        if (DEBUG) {
-            System.out.println("Services provided by BCJSSE security Provider");
-
-            for (Provider.Service s : Security.getProvider("BCJSSE").getServices()) {
-                System.out.println(s);
-            }
-            System.out.println();
-        }
-
-        // get default from most prioritized securtiy provider -> BCJSSE
-        SSLContext sslCtx = SSLContext.getInstance("TLS", "BCJSSE");
-
-        // initialize sslContext with a KeyManager and no TrustManager
-        KeyStore ks = KeyStore.getInstance("PKCS12");
-        ks.load(new FileInputStream(keystore), keystorePwd.toCharArray());
-        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
-        kmf.init(ks, keystorePwd.toCharArray());
-        sslCtx.init(kmf.getKeyManagers(), null, null);
-
-        // verify correct selection
-        if(DEBUG) {
-            System.out.println("Current provider:");
-            System.out.println(sslCtx.getProvider());
-            System.out.println();
-
-            System.out.println("Selected protocol:");
-            System.out.println(sslCtx.getProtocol());
-            System.out.println();
-
-            SSLParameters sslParams = sslCtx.getSupportedSSLParameters();
-
-            System.out.println("Protocols:");
-            Arrays.stream(sslParams.getProtocols()).forEach(System.out::println);
-            System.out.println();
-
-            System.out.println("Use cipher suites order: ");
-            System.out.println(sslParams.getUseCipherSuitesOrder());
-            System.out.println();
-
-            System.out.println("Cipher suites:");
-            Arrays.stream(sslParams.getCipherSuites()).forEach(System.out::println);
-            System.out.println();
-        }
-
-        // create the SSLServerSocket
-        SSLServerSocketFactory socketFactory = sslCtx.getServerSocketFactory();
-        socket = (SSLServerSocket) socketFactory.createServerSocket(port);
-
-        // set protocol versions and cipher suites
-        socket.setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
-        socket.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_AES_128_CCM_SHA256", "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384"});
-
-        if (DEBUG) {
-            System.out.println("Enabled by the Socket:");
-
-            System.out.println("Protocols:");
-            Arrays.stream(socket.getEnabledProtocols()).forEach(System.out::println);
-            System.out.println();
-
-            System.out.println("Cipher suites:");
-            Arrays.stream(socket.getEnabledCipherSuites()).forEach(System.out::println);
-            System.out.println();
-        }
-    }
-
-    private void start() {
-        while(true) {
-            try (Socket sock = socket.accept()) {
-                BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
-                BufferedWriter out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
-                while (in.readLine() != null) {
-                    out.write("ack");
-                    out.flush();
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    public static void main(String[] args) {
-        // ensure Bouncy Castle is first provider queried when retrieving implementations
-        Security.insertProviderAt(new BouncyCastleJsseProvider(), 1);
-        Security.insertProviderAt(new BouncyCastleProvider(), 2);
-
-        if (DEBUG) {
-            System.out.println("Registered security providers:");
-
-            Provider[] ps = Security.getProviders();
-            for (int i = 0; i < ps.length; i++) {
-                System.out.println(i + " : " + ps[i]);
-            }
-            System.out.println();
-        }
-
-        // try to get the path of the needed Keystore:
-        File jks = null;
-        // 1: absolute path via program argument
-        if (args.length != 0) {
-            jks = new File(args[0]);
-        }
-        // 2: absolute path via environment variable
-        if (args.length == 0 || !jks.exists()) {
-            String env = System.getenv("KEYSTORE_PATH");
-            if (env != null)
-                jks = new File(env);
-            //3: load via class loader
-            if (env == null || !jks.exists()) {
-                // Throws a NPE if Keystore could not be found up until this point
-                jks = new File(TlsServer.class.getClassLoader().getResource("keystore.jks").getPath());
-            }
-        }
-
-        // create TLS server
-        TlsServer server = new TlsServer();
-        try {
-            // the keystore is expected to be generated with the script in the resource folder
-            server.configure(2200, jks.getAbsolutePath(), "demo-password");
-        } catch (Exception e) {
-            e.getLocalizedMessage();
-            System.exit(1);
-        }
-        server.start();
-    }
-}
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
deleted file mode 100644
index a7748a1a6a34bcd5c8fa94e7d462a952b0904b39..0000000000000000000000000000000000000000
--- a/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<configuration>
-    <appenders>
-        <console name="stdout" target="SYSTEM_OUT">
-            <patternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L - %m%n"/>
-        </console>
-
-        <file name="fileout" fileName="medina-codyze.log">
-            <patternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L - %m%n"/>
-        </file>
-    </appenders>
-
-    <loggers>
-        <Logger level="ALL" name="de.fraunhofer.aisec.codyze"/>
-        <root level="TRACE">
-            <appenderRef ref="stdout"/>
-        </root>
-    </loggers>
-</configuration>
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/bt1/block_ciphers.mark b/src/test/resources/mappingTreeTestStructure/botan/bt1/block_ciphers.mark
new file mode 100644
index 0000000000000000000000000000000000000000..117cc7140b942d1afda5d978ba78b80544d898ff
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/bt1/block_ciphers.mark
@@ -0,0 +1,20 @@
+package botan
+
+/*
+ * From Botan Handbook:
+ * "In general a bare block cipher is not what you should be using. You probably want a cipher mode instead (see Cipher Modes)"
+ */
+
+entity Botan.Forbidden.BlockCipher {
+	op forbid {
+		forbidden Botan::get_block_cipher();
+		forbidden Botan::get_block_cipher(...);
+		forbidden Botan::get_block_cipher_providers();
+		forbidden Botan::get_block_cipher_providers(...);
+		
+		forbidden Botan::BlockCipher::create();
+		forbidden Botan::BlockCipher::create(...);
+		forbidden Botan::BlockCipher::create_or_throw();
+		forbidden Botan::BlockCipher::create_or_throw(...);
+	}
+}
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/bt1/mapping.yaml b/src/test/resources/mappingTreeTestStructure/botan/bt1/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..de12ff7a3fa477761d1435921d0e58db235197de
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/bt1/mapping.yaml
@@ -0,0 +1,11 @@
+metrics:
+  - name: "bt1"
+    rules:
+      - "block_ciphers"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/bt2/hash.mark b/src/test/resources/mappingTreeTestStructure/botan/bt2/hash.mark
new file mode 100644
index 0000000000000000000000000000000000000000..f987874caf7d51f2f12c690d64dfbb795caa9620
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/bt2/hash.mark
@@ -0,0 +1,69 @@
+package botan
+
+entity Botan.HashFunction {
+	var alg;
+	var data;
+	var len;
+	var hash_output;
+	
+	op create {
+		Botan::HashFunction::create(alg);
+		Botan::HashFunction::create(alg, _);
+		Botan::HashFunction::create_or_throw(alg);
+		Botan::HashFunction::create_or_throw(alg, _);
+	}
+	
+	op update {
+		Botan::HashFunction::update(data);
+		Botan::HashFunction::update(data: uint8_t[], length);
+	}
+
+	op finalize {
+		hash_output = Botan::HashFunction::final();
+		hash_output = Botan::HashFunction::final_stdvec();
+		Botan::HashFunction::final(hash_output);
+	}
+	
+	op process {
+		hash_output = Botan::HashFunction::process(data);
+		hash_output = Botan::HashFunction::process(data: uint8_t[], length);
+	}
+}
+
+rule HashOrder {
+	using Botan.HashFunction as hf
+	ensure order 
+		hf.create(),
+		(
+			(hf.update()*, hf.finalize())
+			| hf.process()
+		)
+	onfail HashOrder
+		
+}
+
+ 
+ 
+ /*
+  * For use of hashes in Filter/Pipes
+  * correct order is defined in pipe.mark
+  */
+ entity Botan.Hash_Filter {
+ 	var hash_function: Botan.HashFunction;
+ 	var request;
+ 	
+ 	op create {
+ 		Botan::Hash_Filter(request: std::string, len);
+ 		Botan::Hash_Filter(request: std::string, len);
+ 		Botan::Hash_Filter(hash_function: Botan::HashFunction);
+ 		Botan::Hash_Filter(hash_function: Botan::HashFunction, len);
+ 	}
+ } 
+ 
+ rule _4_01_HashFilter {
+	using Botan.Hash_Filter as hf
+	when _has_value(hf.request)
+	ensure hf.request in ["SHA-256", "SHA-512-256", "SHA-384", "SHA-512", "SHA3-256", "SHA3-384", "SHA3-512"]
+	onfail _01_HashFilter
+}
+ 
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/bt2/mapping.yaml b/src/test/resources/mappingTreeTestStructure/botan/bt2/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..bda4ddf5f351e4ed563ebe8a225e5a9ddb1fedda
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/bt2/mapping.yaml
@@ -0,0 +1,11 @@
+metrics:
+  - name: "bt2"
+    rules:
+      - "hash"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/cipher_mode.mark b/src/test/resources/mappingTreeTestStructure/botan/cipher_mode.mark
new file mode 100644
index 0000000000000000000000000000000000000000..b1b29a1100fdf08b2b06fd46a88b2638558202c1
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/cipher_mode.mark
@@ -0,0 +1,107 @@
+package botan
+
+entity Botan.Cipher_Mode {
+	
+	var algorithm;
+	var symkey : Botan.SymmetricKey; 
+	var iv : Botan.InitializationVector;
+	var iv_length;
+	var direction;
+
+	var input;
+	var input_length;
+	
+	var inout;
+	
+	var aead_data;
+	var aead_data_len;
+
+	/* 
+		Note: allows creating objects of Type Botan::Keyed_Filter and Botan::Cipher_Mode, therefore all other ops should consider member functions of these two classes 
+
+		Botan::OctetString might be allowed for key and IV, but we will not accept it because Botan::SymmetricKey and Botan::InitializationVector carry more semantics, which should increase safety and maintainability.
+		We allow secure vector because real world examples seem to use it
+		*/
+		
+	/* Note: there is also the possibility to create a cipher from ECIES_System_Params. Maybe forbid that? */
+
+	op create_uninit {
+	    Botan::get_cipher_mode(algorithm, direction);
+		Botan::get_cipher_mode(algorithm, direction, ...);
+		Botan::get_cipher(algorithm, direction);
+	}
+	
+	op create_key_init {
+		Botan::get_cipher(
+			algorithm, 
+			iv: Botan::InitializationVector, 
+			direction
+		);
+	}
+	
+	op create_key_iv_init {
+		Botan::get_cipher(
+			algorithm, 
+			symkey: Botan::SymmetricKey, 
+			iv: Botan::InitializationVector, 
+			direction
+		);
+	}
+
+	op set_key {
+		Botan::Cipher_Mode::set_key(symkey: Botan::SymmetricKey | Botan::secure_vector<uint8_t>);
+		forbidden Botan::Cipher_Mode::set_key(_, _);
+		Botan::Keyed_Filter::set_key(symkey: Botan::SymmetricKey | Botan::secure_vector<uint8_t>);
+	}
+	
+	op set_iv {
+		Botan::Keyed_Filter::set_iv(iv: Botan::InitializationVector);
+	}
+
+	op start_no_iv {
+		Botan::Cipher_Mode::start();
+		Botan::Keyed_Filter::start_msg();
+	}
+	
+	op start_iv {
+		Botan::Cipher_Mode::start(iv);
+		Botan::Cipher_Mode::start(iv, iv_length);
+		Botan::Cipher_Mode::start_msg(iv, iv_length);
+	}
+
+	op process {
+		Botan::Cipher_Mode::process(input, input_length);
+		Botan::Cipher_Mode::update(inout);
+		Botan::Cipher_Mode::update(inout, _);
+
+		Botan::Keyed_Filter::write(input, input_length);
+	}
+
+	op finish {
+		Botan::Cipher_Mode::finish(inout);
+		Botan::Cipher_Mode::finish(inout, _);
+
+		Botan::Keyed_Filter::end_msg();
+	}
+
+	op reset {
+		Botan::Cipher_Mode::reset();
+	}
+	
+	op assoc_data {
+		Botan::AEAD_Filter::set_associated_data(aead_data, aead_data_len);
+	}
+	
+}
+
+
+rule Cipher_Mode_Order {
+	using Botan.Cipher_Mode as cm
+	ensure order
+		((cm.create_uninit(), cm.set_key()) | cm.create_key_init()), // key is set here
+		((cm.set_iv(), cm.start_no_iv()) |  cm.start_iv()),
+		cm.assoc_data()*,
+		cm.process()*,
+		cm.finish()
+	onfail Cipher_Mode_Order
+}
\ No newline at end of file
diff --git a/src/test/resources/mappingTreeTestStructure/botan/mapping.yaml b/src/test/resources/mappingTreeTestStructure/botan/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d86e377b832b72182becae5a6becf522eef18c59
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/botan/mapping.yaml
@@ -0,0 +1,13 @@
+metrics:
+  - name: "botan"
+    rules:
+      - "hash"
+      - "block_ciphers"
+      - "cipher_mode"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/BouncyCastleProvider.mark b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/BouncyCastleProvider.mark
similarity index 100%
rename from src/test/resources/Mark/bouncycastle/BouncyCastleProvider.mark
rename to src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/BouncyCastleProvider.mark
diff --git a/src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/mapping.yaml b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d54470d7a1531e19dd64ad67ddde60ed8b7c6447
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/mapping.yaml
@@ -0,0 +1,11 @@
+metrics:
+  - name: "bc1"
+    rules:
+      - "BouncyCastleProvider"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
\ No newline at end of file
diff --git a/src/test/resources/Mark/bouncycastle/Cipher.mark b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/Cipher.mark
similarity index 100%
rename from src/test/resources/Mark/bouncycastle/Cipher.mark
rename to src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/Cipher.mark
diff --git a/src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/mapping.yaml b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/mapping.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..da14598b0570cd0037aad79b1d34ea86d1c348c5
--- /dev/null
+++ b/src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/mapping.yaml
@@ -0,0 +1,11 @@
+metrics:
+  - name: "bc2"
+    rules:
+      - "Cipher"
+    configuration:
+        default: false
+        operator: "="
+        type: NUMBER
+        target:
+          - "1.23"
+          - "3.14"
\ No newline at end of file
diff --git a/templates/codyze-medina-metrics.yaml b/templates/codyze-medina-metrics.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cd2d6cde71c244764912175b298ff3e4789e3f5b
--- /dev/null
+++ b/templates/codyze-medina-metrics.yaml
@@ -0,0 +1,16 @@
+metrics:
+  - name: "CodeSignoff"
+    target:
+      - "<Name>"
+      - "<Name>"
+  - name: "SignedCommits"
+    target:
+      - "<16-Digit Key-Id>"
+  - name: "SignedSignoff"
+    target:
+      - name: "<Name>"
+        email: "<E-Mail>"
+        pub-key-id: "<16-Digit Base-64 Signing-Key-Id>"
+  - name: "ApprovedCommitAuthor"
+    target:
+      - "<Name>"
diff --git a/templates/codyze-medina.yaml b/templates/codyze-medina.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3051555b0d22b92438c0cf95374abf5290963f53
--- /dev/null
+++ b/templates/codyze-medina.yaml
@@ -0,0 +1,32 @@
+# yaml-language-server: $schema=schema/codyze-config-schema.json
+# For more explanation regarding the parameters, please refer to the README.
+
+orchestrator:
+  required: <BOOLEAN>       # Optional value, defaults to true
+  endpoint: "<URL>"         # Required value
+  auth:
+    oauth-endpoint: "<URL>" # Required value
+    username: "<STRING>"    # Required value
+    password: "<STRING>"    # Required value, can also be injected via program arguments or environment variables
+
+id: "<UUID>"                # Required value
+source:                     # Required value
+  - <FILEPATH>
+  - ...
+
+ci: "<NONE | GITLAB | GITHUB | JENKINS>"  # Optional value, automatically inferred by default
+rules: <FILEPATH>           # Optional value, defaults to codyze-medina-metrics.yaml
+medina-output: <FILEPATH>   # Optional value, defaults to codyze-medina.sarif
+combined-output: <BOOLEAN>  # Optional value, defaults to true
+key-location: <PATH>        # Optional value, defaults to public-keys/
+
+mark:           # While technically optional, MARK rules are required to generate analysis results
+  builtin:      # Refers to MARK modules included in the "mark/" directory of the release package
+    - <MODULE_NAME>
+    - ...
+  project:      # Refers to additional MARK modules in other places
+    - <PATH>
+    - ...
+
+# All additional parameters will be directly passed to the underlying Codyze implementation.
+# Core Codyze parameters are documented at https://www.codyze.io/