From e91513ecbd7ec9439be6fc8a0947c437e84e6e6e Mon Sep 17 00:00:00 2001
From: "Wendland, Florian" <florian.wendland@aisec.fraunhofer.de>
Date: Mon, 20 Nov 2023 12:00:52 +0100
Subject: [PATCH] Prepare release 1.6.0

---
 .dockerignore                                 |    6 +-
 .gitignore                                    |    3 +-
 Dockerfile                                    |   53 +-
 README.md                                     |  101 +-
 build.gradle.kts                              |  126 +-
 docs/mapping.md                               |   67 +
 docs/medina-rules.md                          |   32 +
 gradle/wrapper/gradle-wrapper.jar             |  Bin 60756 -> 63721 bytes
 gradle/wrapper/gradle-wrapper.properties      |    4 +-
 gradlew                                       |   35 +-
 gradlew.bat                                   |    1 +
 local.Dockerfile                              |   26 +
 mark/bc-jca/Cipher.mark                       |  143 ++
 .../bc-jca}/KeyAgreement.mark                 |   26 +
 mark/bc-jca/LICENSE                           |  202 +++
 .../bouncycastle => mark/bc-jca}/Mac.mark     |   44 +-
 mark/bc-jca/MessageDigest.mark                |   64 +
 mark/bc-jca/README.md                         |    8 +
 .../bc-jca}/SecureRandom.mark                 |   41 +-
 mark/bc-jca/Signature.mark                    |  115 ++
 mark/bc-jca/findingDescription.json           |   92 ++
 mark/bc-jca/mapping.yaml                      |   41 +
 mark/bc-jca/rules.mark                        |  402 ++++++
 mark/bc-jsse/LICENSE                          |  202 +++
 mark/bc-jsse/README.md                        |    4 +-
 mark/bc-jsse/SSLContext.mark                  |   28 +
 mark/bc-jsse/SSLServerSocket.mark             |   28 +
 mark/bc-jsse/SSLServerSocketFactory.mark      |   28 +
 mark/bc-jsse/findingDescription.json          |   32 +
 mark/bc-jsse/mapping.yaml                     |  146 ++
 mark/bc-jsse/rules.mark                       |   89 +-
 settings.gradle.kts                           |    2 +-
 .../aisec/codyze/medina/CodyzeMedina.kt       |  144 --
 .../aisec/codyze/medina/Connection.kt         |  329 -----
 .../codyze/medina/assembling/Assembler.kt     |  199 +++
 .../codyze/medina/assembling/Environment.kt   |  154 +++
 .../aisec/codyze/medina/auth/Auth.kt          |  104 --
 .../codyze/medina/connection/ApiManager.kt    |  162 +++
 .../codyze/medina/connection/Connection.kt    |   85 ++
 .../codyze/medina/connection/OAuthManager.kt  |  114 ++
 .../codyze/medina/connection/TokenManager.kt  |  114 ++
 .../medina/evaluation/EvaluationResult.kt     |   54 +
 .../codyze/medina/evaluation/Evaluator.kt     |  142 ++
 .../aisec/codyze/medina/evaluation/Rule.kt    |   70 +
 .../base/ApprovedCommitAuthorEvaluator.kt     |   67 +
 .../evaluation/base/GPGSignatureEvaluator.kt  |  103 ++
 .../medina/evaluation/base/MetricEvaluator.kt |  114 ++
 .../evaluation/base/SignOffEvaluator.kt       |   84 ++
 .../evaluation/base/SignedSignOffEvaluator.kt |  141 ++
 .../aisec/codyze/medina/main/CodyzeMedina.kt  |  428 ++++++
 .../codyze/medina/{ => main}/Configuration.kt |  113 +-
 .../aisec/codyze/medina/main/MarkResolver.kt  |   82 ++
 .../aisec/codyze/medina/mapping/Mapping.kt    |   57 +
 .../codyze/medina/mapping/MappingTree.kt      |  115 ++
 .../codyze/medina/mapping/MedinaMapping.kt    |   41 +
 .../aisec/codyze/medina/util/Assemblers.kt    |   87 --
 .../aisec/codyze/medina/util/Environment.kt   |  101 --
 .../aisec/codyze/medina/util/MedinaSarif.kt   |  157 +++
 .../aisec/codyze/medina/util/Util.kt          |  122 +-
 src/main/resources/evidence.yaml              |   47 +-
 src/main/resources/log4j.properties           |    9 +
 src/main/resources/log4j2.xml                 |    2 +-
 src/main/resources/orchestrator.yaml          | 1214 +++++++++++++++--
 .../aisec/codyze/medina/AuthTest.kt           |   11 +-
 .../fraunhofer/aisec/codyze/medina/CliTest.kt |   37 +
 .../codyze/medina/CodyzeCliPassThroughTest.kt |   10 +-
 .../aisec/codyze/medina/ConfigurationTest.kt  |    2 +
 .../aisec/codyze/medina/ConverterTest.kt      |  111 ++
 .../aisec/codyze/medina/DemoTest.kt           |    1 +
 .../aisec/codyze/medina/IntegrationTest.kt    |  161 +++
 .../aisec/codyze/medina/LoggingTest.kt        |   87 ++
 .../aisec/codyze/medina/MappingTreeTest.kt    |   54 +
 .../aisec/codyze/medina/MedinaMetrikTest.kt   |   46 +
 .../aisec/codyze/medina/ParserTest.kt         |   95 --
 .../aisec/codyze/medina/SarifTest.kt          |  122 ++
 .../codyze/medina/assembling/AssemblerTest.kt |  180 +++
 src/test/resources/Mark/botan/mapping.yaml    |   29 +
 .../AlgorithmParameterGenerator.mark          |   43 -
 .../bouncycastle/AlgorithmParameters.mark     |   34 -
 .../Mark/bouncycastle/CertPathBuilder.mark    |   24 -
 .../Mark/bouncycastle/CertPathValidator.mark  |   28 -
 .../Mark/bouncycastle/CertStore.mark          |   34 -
 .../Mark/bouncycastle/CertificateFactory.mark |   56 -
 .../bouncycastle/ChaCha20ParameterSpec.mark   |   14 -
 .../Mark/bouncycastle/DHGenParameterSpec.mark |   15 -
 .../Mark/bouncycastle/DHParameterSpec.mark    |   22 -
 .../Mark/bouncycastle/DHPrivateKeySpec.mark   |   16 -
 .../Mark/bouncycastle/DHPublicKeySpec.mark    |   16 -
 .../bouncycastle/DSAGenParameterSpec.mark     |   22 -
 .../Mark/bouncycastle/DSAParameterSpec.mark   |   17 -
 .../Mark/bouncycastle/DSAPrivateKeySpec.mark  |   18 -
 .../Mark/bouncycastle/DSAPublicKeySpec.mark   |   18 -
 .../Mark/bouncycastle/ECFieldF2m.mark         |   22 -
 .../Mark/bouncycastle/ECFieldFp.mark          |   13 -
 .../Mark/bouncycastle/ECGenParameterSpec.mark |   14 -
 .../resources/Mark/bouncycastle/ECKey.mark    |    5 -
 .../Mark/bouncycastle/ECParameterSpec.mark    |   19 -
 .../resources/Mark/bouncycastle/ECPoint.mark  |   14 -
 .../Mark/bouncycastle/ECPrivateKeySpec.mark   |   16 -
 .../Mark/bouncycastle/ECPublicKeySpec.mark    |   14 -
 .../Mark/bouncycastle/EncodedKeySpec.mark     |   18 -
 .../Mark/bouncycastle/GCMParameterSpec.mark   |   22 -
 .../Mark/bouncycastle/HMACParameterSpec.mark  |   12 -
 .../Mark/bouncycastle/IvParameterSpec.mark    |   19 -
 .../Mark/bouncycastle/KeyFactory.mark         |   31 -
 .../Mark/bouncycastle/KeyGenerator.mark       |   40 -
 .../resources/Mark/bouncycastle/KeyPair.mark  |   14 -
 .../Mark/bouncycastle/KeyPairGenerator.mark   |   46 -
 .../KeyStore.PasswordProtection.mark          |   19 -
 .../resources/Mark/bouncycastle/KeyStore.mark |   60 -
 .../Mark/bouncycastle/MGF1ParameterSpec.mark  |   12 -
 .../Mark/bouncycastle/MessageDigest.mark      |   39 -
 .../Mark/bouncycastle/OAEPParameterSpec.mark  |   19 -
 .../resources/Mark/bouncycastle/PBEKey.mark   |    5 -
 .../Mark/bouncycastle/PBEKeySpec.mark         |   26 -
 .../Mark/bouncycastle/PBEParameterSpec.mark   |   20 -
 .../bouncycastle/PKCS8EncodedKeySpec.mark     |   18 -
 .../Mark/bouncycastle/PSSParameterSpec.mark   |   23 -
 .../Mark/bouncycastle/PrivateKey.mark         |    5 -
 .../Mark/bouncycastle/PublicKey.mark          |    5 -
 .../bouncycastle/RSAKeyGenParameterSpec.mark  |   20 -
 .../RSAMultiPrimePrivateCrtKeySpec.mark       |   42 -
 .../bouncycastle/RSAPrivateCrtKeySpec.mark    |   38 -
 .../Mark/bouncycastle/RSAPrivateKeySpec.mark  |   21 -
 .../Mark/bouncycastle/RSAPublicKeySpec.mark   |   21 -
 .../Mark/bouncycastle/RulesBase_Cipher.mark   |   50 -
 .../Mark/bouncycastle/RulesTR_Cipher.mark     |  371 -----
 .../RulesTR_InstanceAuthentication.txt        |    8 -
 .../bouncycastle/RulesTR_KeyAgreement.txt     |   10 -
 .../Mark/bouncycastle/RulesTR_MAC.mark        |  187 ---
 .../bouncycastle/RulesTR_MessageDigest.mark   |   17 -
 .../Mark/bouncycastle/RulesTR_PRNG.txt        |   10 -
 .../Mark/bouncycastle/RulesTR_Signature.mark  |   81 --
 .../bouncycastle/Rules_BouncyCastleJCA.mark   |  223 ---
 .../Mark/bouncycastle/SecretKey.mark          |    5 -
 .../Mark/bouncycastle/SecretKeyFactory.mark   |   35 -
 .../Mark/bouncycastle/SecretKeySpec.mark      |   23 -
 .../Mark/bouncycastle/Signature.mark          |   89 --
 .../Mark/bouncycastle/X509EncodedKeySpec.mark |   18 -
 .../Mark/bouncycastle/XECPrivateKeySpec.mark  |   14 -
 .../Mark/bouncycastle/XECPublicKeySpec.mark   |   14 -
 src/test/resources/Mark/exampleMapping.yaml   |   20 +
 .../resources/Mark/jackson/ObjectMapper.mark  |   12 -
 src/test/resources/codyze.yaml                |   12 +-
 .../resources/demos/bcjsse/TlsServer.java     |  146 --
 src/test/resources/log4j2.xml                 |   19 -
 .../botan/bt1/block_ciphers.mark              |   20 +
 .../botan/bt1/mapping.yaml                    |   11 +
 .../botan/bt2/hash.mark                       |   69 +
 .../botan/bt2/mapping.yaml                    |   11 +
 .../botan/cipher_mode.mark                    |  107 ++
 .../botan/mapping.yaml                        |   13 +
 .../bc1}/BouncyCastleProvider.mark            |    0
 .../bouncycastle/bc1/mapping.yaml             |   11 +
 .../bouncycastle/bc2}/Cipher.mark             |    0
 .../bouncycastle/bc2/mapping.yaml             |   11 +
 templates/codyze-medina-metrics.yaml          |   16 +
 templates/codyze-medina.yaml                  |   32 +
 158 files changed, 6995 insertions(+), 3651 deletions(-)
 create mode 100644 docs/mapping.md
 create mode 100644 docs/medina-rules.md
 create mode 100644 local.Dockerfile
 create mode 100644 mark/bc-jca/Cipher.mark
 rename {src/test/resources/Mark/bouncycastle => mark/bc-jca}/KeyAgreement.mark (61%)
 create mode 100644 mark/bc-jca/LICENSE
 rename {src/test/resources/Mark/bouncycastle => mark/bc-jca}/Mac.mark (52%)
 create mode 100644 mark/bc-jca/MessageDigest.mark
 create mode 100644 mark/bc-jca/README.md
 rename {src/test/resources/Mark/bouncycastle => mark/bc-jca}/SecureRandom.mark (64%)
 create mode 100644 mark/bc-jca/Signature.mark
 create mode 100644 mark/bc-jca/findingDescription.json
 create mode 100644 mark/bc-jca/mapping.yaml
 create mode 100644 mark/bc-jca/rules.mark
 create mode 100644 mark/bc-jsse/LICENSE
 create mode 100644 mark/bc-jsse/findingDescription.json
 create mode 100644 mark/bc-jsse/mapping.yaml
 delete mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/CodyzeMedina.kt
 delete mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/Connection.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Assembler.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/Environment.kt
 delete mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/auth/Auth.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/ApiManager.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/Connection.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/OAuthManager.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/connection/TokenManager.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/EvaluationResult.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Evaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/Rule.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/ApprovedCommitAuthorEvaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/GPGSignatureEvaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/MetricEvaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignOffEvaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/evaluation/base/SignedSignOffEvaluator.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/CodyzeMedina.kt
 rename src/main/kotlin/de/fraunhofer/aisec/codyze/medina/{ => main}/Configuration.kt (69%)
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/main/MarkResolver.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/Mapping.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MappingTree.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/mapping/MedinaMapping.kt
 delete mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Assemblers.kt
 delete mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/Environment.kt
 create mode 100644 src/main/kotlin/de/fraunhofer/aisec/codyze/medina/util/MedinaSarif.kt
 create mode 100644 src/main/resources/log4j.properties
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ConverterTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/IntegrationTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/LoggingTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MappingTreeTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/MedinaMetrikTest.kt
 delete mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/ParserTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/SarifTest.kt
 create mode 100644 src/test/kotlin/de/fraunhofer/aisec/codyze/medina/assembling/AssemblerTest.kt
 create mode 100644 src/test/resources/Mark/botan/mapping.yaml
 delete mode 100644 src/test/resources/Mark/bouncycastle/AlgorithmParameterGenerator.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/AlgorithmParameters.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/CertPathBuilder.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/CertPathValidator.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/CertStore.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/CertificateFactory.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ChaCha20ParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DHGenParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DHParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DHPrivateKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DHPublicKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DSAGenParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DSAParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DSAPrivateKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/DSAPublicKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECFieldF2m.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECFieldFp.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECGenParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECKey.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECPoint.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECPrivateKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/ECPublicKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/EncodedKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/GCMParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/HMACParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/IvParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyFactory.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyGenerator.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyPair.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyPairGenerator.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyStore.PasswordProtection.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/KeyStore.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/MGF1ParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/MessageDigest.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/OAEPParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PBEKey.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PBEKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PBEParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PKCS8EncodedKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PSSParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PrivateKey.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/PublicKey.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RSAKeyGenParameterSpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RSAMultiPrimePrivateCrtKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RSAPrivateCrtKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RSAPrivateKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RSAPublicKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesBase_Cipher.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_Cipher.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_InstanceAuthentication.txt
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_KeyAgreement.txt
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_MAC.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_MessageDigest.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_PRNG.txt
 delete mode 100644 src/test/resources/Mark/bouncycastle/RulesTR_Signature.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/Rules_BouncyCastleJCA.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/SecretKey.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/SecretKeyFactory.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/SecretKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/Signature.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/X509EncodedKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/XECPrivateKeySpec.mark
 delete mode 100644 src/test/resources/Mark/bouncycastle/XECPublicKeySpec.mark
 create mode 100644 src/test/resources/Mark/exampleMapping.yaml
 delete mode 100644 src/test/resources/Mark/jackson/ObjectMapper.mark
 delete mode 100644 src/test/resources/demos/bcjsse/TlsServer.java
 delete mode 100644 src/test/resources/log4j2.xml
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/bt1/block_ciphers.mark
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/bt1/mapping.yaml
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/bt2/hash.mark
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/bt2/mapping.yaml
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/cipher_mode.mark
 create mode 100644 src/test/resources/mappingTreeTestStructure/botan/mapping.yaml
 rename src/test/resources/{Mark/bouncycastle => mappingTreeTestStructure/bouncycastle/bc1}/BouncyCastleProvider.mark (100%)
 create mode 100644 src/test/resources/mappingTreeTestStructure/bouncycastle/bc1/mapping.yaml
 rename src/test/resources/{Mark/bouncycastle => mappingTreeTestStructure/bouncycastle/bc2}/Cipher.mark (100%)
 create mode 100644 src/test/resources/mappingTreeTestStructure/bouncycastle/bc2/mapping.yaml
 create mode 100644 templates/codyze-medina-metrics.yaml
 create mode 100644 templates/codyze-medina.yaml

diff --git a/.dockerignore b/.dockerignore
index efffb91..13fb473 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 62d8458..7c909f4 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 d9ead25..5cfe4fc 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 be5a7b4..dfddf93 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 5784922..84f7c97 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 0000000..9d550b5
--- /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 0000000..68358b0
--- /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
GIT binary patch
delta 44091
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
J%%6f(003c>gtPzv

delta 41205
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
ZZfl7Jc(byB#LXGZ8I*+?7^H83cmO;W-rE2G

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ae04661..3fa8f86 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 a69d9cb..1aa94a4 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 53a6b23..6689b85 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 0000000..5e5fced
--- /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 0000000..fc6d829
--- /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 f2ed50a..c99e99c 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 0000000..d645695
--- /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 f0f9046..15c7933 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 0000000..0e3b5d1
--- /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 0000000..1854e6c
--- /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 fe531bf..c85ea98 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 0000000..61a6b8d
--- /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 0000000..52dc0b7
--- /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 0000000..f72cabc
--- /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 0000000..50cac13
--- /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 0000000..d645695
--- /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 df62830..059f36f 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 d078c67..528825c 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 6be07b7..a8a3883 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 7521deb..91edaa6 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 0000000..7e9790b
--- /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 0000000..d160855
--- /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 0bf5ca5..0a293e6 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 2f5d7f5..5ea3306 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 d67383b..0000000
--- 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 ec560a2..0000000
--- 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 0000000..2767a96
--- /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 0000000..3694b25
--- /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 76e762a..0000000
--- 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 0000000..17d663a
--- /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 0000000..fecb41e
--- /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 0000000..f8953fe
--- /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 0000000..f2b0546
--- /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 0000000..e327a2c
--- /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 0000000..c214c05
--- /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 0000000..1cd6932
--- /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 0000000..797adc8
--- /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 0000000..83d5b4d
--- /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 0000000..c681cd7
--- /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 0000000..2c8e0f0
--- /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 0000000..651d9f9
--- /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 0000000..9d4e363
--- /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 7de6b5a..1c2fe52 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 0000000..8909a1f
--- /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 0000000..b207222
--- /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 0000000..fb7a222
--- /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 0000000..8c624cd
--- /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 5314507..0000000
--- 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 990cd38..0000000
--- 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 0000000..9997cea
--- /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 d9368df..f433609 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 9edfed1..4497d59 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 0000000..bda1b36
--- /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 1d1b7e1..23bbbda 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 bc269e1..e3a1200 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 c41601b..c2c06d7 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 b2e8566..fae7b2f 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 af5d0ea..009a68e 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 87797aa..02f4607 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 0000000..c50fbcd
--- /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 cd62e0b..58dcdc2 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 0000000..53080b3
--- /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 0000000..a5641c3
--- /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 0000000..d947213
--- /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 0000000..beabf07
--- /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 7925c56..0000000
--- 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 0000000..dffd97e
--- /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 0000000..2fbc2d3
--- /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 0000000..be0dd4b
--- /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 6edb8a7..0000000
--- 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 268c912..0000000
--- 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 ec8c264..0000000
--- 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 76dd8af..0000000
--- 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 2e6af2a..0000000
--- 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 dafaed2..0000000
--- 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 a57fc83..0000000
--- 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 b90a800..0000000
--- 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 9d15c16..0000000
--- 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 d505965..0000000
--- 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 4a563dd..0000000
--- 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 b4c3130..0000000
--- 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 612f3bd..0000000
--- 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 cd1a37f..0000000
--- 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 cfe6735..0000000
--- 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 e069f48..0000000
--- 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 0199f16..0000000
--- 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 c0e451e..0000000
--- 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 e80cae5..0000000
--- 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 3eb4049..0000000
--- 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 fb9a58a..0000000
--- 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 1f91cca..0000000
--- 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 17bf1a5..0000000
--- 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 eb52527..0000000
--- 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 ed3c7f2..0000000
--- 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 a243d33..0000000
--- 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 2ccc1a8..0000000
--- 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 7e7bb89..0000000
--- 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 718e1a4..0000000
--- 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 5067b13..0000000
--- 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 b9f919f..0000000
--- 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 64a91ba..0000000
--- 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 e92b2a1..0000000
--- 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 208f2ae..0000000
--- 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 1ce9bd7..0000000
--- 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 4d9060a..0000000
--- 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 b19111e..0000000
--- 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 aa14dcb..0000000
--- 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 debd301..0000000
--- 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 bb19813..0000000
--- 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 5b7256e..0000000
--- 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 48d7aa1..0000000
--- 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 44b8aff..0000000
--- 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 b8b938e..0000000
--- 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 81c6d51..0000000
--- 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 8c506c5..0000000
--- 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 69a8732..0000000
--- 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 32f8764..0000000
--- 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 84327ff..0000000
--- 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 d371cb8..0000000
--- 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 2fc11a0..0000000
--- 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 a5ea628..0000000
--- 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 c30e1ce..0000000
--- 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 86d407b..0000000
--- 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 bd975b5..0000000
--- 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 3de1756..0000000
--- 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 607197c..0000000
--- 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 2a42d04..0000000
--- 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 8703ea3..0000000
--- 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 fbcb922..0000000
--- 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 effa321..0000000
--- 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 298ef70..0000000
--- 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 d093427..0000000
--- 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 e228310..0000000
--- 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 0000000..7358b83
--- /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 dad5ede..0000000
--- 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 66a79ac..2a2abb4 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 f71cad4..0000000
--- 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 a7748a1..0000000
--- 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 0000000..117cc71
--- /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 0000000..de12ff7
--- /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 0000000..f987874
--- /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 0000000..bda4ddf
--- /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 0000000..b1b29a1
--- /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 0000000..d86e377
--- /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 0000000..d54470d
--- /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 0000000..da14598
--- /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 0000000..cd2d6cd
--- /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 0000000..3051555
--- /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/
-- 
GitLab