diff --git a/Dockerfile_ExerciseChecker b/Dockerfile_ExerciseChecker
index 2a864edd3b33607b098b412190547e7e62fc83aa..64d33a31674d1a3c0fc657599eedc321a982ede1 100644
--- a/Dockerfile_ExerciseChecker
+++ b/Dockerfile_ExerciseChecker
@@ -36,4 +36,6 @@ RUN apk add git
 
 ARG BUILD_WORKDIR
 
-COPY --from=builder ${BUILD_WORKDIR}/bin/app /usr/local/bin/dojo_exercise_checker
\ No newline at end of file
+COPY --from=builder ${BUILD_WORKDIR}/bin/app /usr/local/bin/dojo_exercise_checker
+
+ADD sonar/ /sonar/
diff --git a/ExerciseChecker/.env.vault b/ExerciseChecker/.env.vault
index 2e0ff1097b2659ff621d5ca5b86e548f0e06e848..8770a81f692a36c531a58c2b3b94ac3f2cb73e27 100644
--- a/ExerciseChecker/.env.vault
+++ b/ExerciseChecker/.env.vault
@@ -4,11 +4,11 @@
 #/--------------------------------------------------/
 
 # development
-DOTENV_VAULT_DEVELOPMENT="RB+8dSf+xdgTMm5DShoKPowjwHV8LPzFOmNXJBWvEt6vgf2LggQl5d2lBCYhGCQza1ABd6+RIL1lo9ZEJEubyq9+7BkhgCim0TAw2UfGqQUrwrNtstfzEDC7534Kx1gtVNpkUP2E15zhvQARDIATe96ud+Yunw1RNGhEvqD5UhzBejLAe6SGupcL0M5PNf8/qW/aVweuEYqNBxM7pAGwrVJRB1AxEcDVWSkJotVZOsbSk/VR65q6XXqSrsuzGaM7tC/yYIZbd6CrjzVQzA8/ejEpa/Piy3bpNmB23JVBrGfkqn2YppSXqHkdoVDHpHBmztlmQPy8WoJH5c6GOXESh8v8/K1Z2W5VVGd8JN//xEK2WqLf77P2PojPxVV+D+Ulj7xzwCXnP6UFqqCirei6UPPygUvBlg47AUmBcxFf26HC3tIRJ1/A5Kt7v9QAgnGsDW4zPF99vEirf3pKVchICs+m7M5dsTmuZIi6AkL0ErBO9jJZBUpkRXYwPYx3EqAeeqZw1jMmVO5cAVGpVDu3BFmGV59HbpRqTGMmiC9RRUl23wmt4msYfSr446Bx+ijtOY5iXpGTD9HSDtjWcXviWncZTsucUFiP9Ao57f35PqKdv5mlGiW2uwytSEH8y8HTA+fyLzFCSHYE/4QqOFrGFNB1HzbW5R3G25IXIrp0BRP0TPZlBwROEDF3mW6iHIKQru9GOSypvCBr551y4hkhXXhAVqhTrJYPt8UJDh1YNm/0rnjJglTLiX/OEd8wsLc/hh8N6iKX3wp0G+nubQ9SnIbzU7ubjIIsk6tMFS0OAOa0IjhgnkJBKwRn5DV0nWG8gFKj2/ZFMo1pA+l/q48eryzU3w2HOH7Bb/UJtLCKt+CILfgkbwqlk3WFGo+NAKNOwdH8y8re4WeagwRwJKUu3Jm2IjqTNG9i99+VwYCX78A7xdkYjjwqLzsTIxMiSlFjb4ZEWLcFTHtXp/rgl4JSGqLuJGzNDyYbR07YWpgdcQIN4grxihi3csPFzD5/RE89nWewQ/OVZAdFdECsjaGBohvZcPV3GVe9p5KAO/RW8PIproZZbDwQX/FCV3NbyhopUwCuNXE4njY="
+DOTENV_VAULT_DEVELOPMENT="gngDubtLd8k45cTe0ujKNxl8EJzPLS+JuNdl+mQZSyVmG1lwh4cteZsfQD8qGQNNvSy2Fvdlk1pQoiDJtix4O5I9EZkwpz9rUNs91AG63tFm8zkVymknazKp3UzAKvRzngDYtkH11uWmDC8B31avNsKB2M1EKYVixOIc+SJP5xfgMKlo2ePye5Ca+GWS7ojHirBc0bQlfkmDeQxo5bn/9+adZjdmZWgYVP399/jwil5LWfi9OfBrezC9OzBaVWR0QobllPZyv4x6oRjiM15JnfwuAhpPnrsGJn8u964FyiXvNIfIZetiN25pSO38azjUXqzl0LrhRzIidf+hsGY7ipj9j6z+0kr3vIKoZaMrosDGrbhrOgV97a6A3AwUbrPEOEGhBpjjb1tGDDMdnD5A+JYwo30B72oVKNBh/G0VkQXUMw1S1O270fdAPcIZ+I0cTdrI40KUw8UBCM4n1lTSN9Iqrk9iqOpo5KNSC7z2kEzL7r1FI+lIuL3wfX+KPLOT9vQHzglxKpYJNh0OzCQ/JByiLZ62gFHn+TqpRfnYCvQu/+XCKLc0ipDL7rrbH8NJlJV90vW8OXV66kEerKJrsQ/YyQunJMcSze/hT+p/HvW/esj/8ZVSN/ey4UDj5OvnfBId26Od9ZcI9Ywq8eHXA8Oa4Ddk0Gxd+i7Lz1s2jsGwVVV3dCbu9KV7bP8V+F1w2ad7cT/W9zDfQ2wZyLc4nIep25gz9a87zdxoKxZIqjtsDesxAbwsQVEn8UGp+IcLu44ZWd/GuVGuFDDed71VQeSV8Y4Y9jR5TPaxpJvxB726QVXITbvMswl4gHsdTn8gKDv1yTpU/PvZpxHR3IdikIcUoCK4Nac7tuGcKHc2MI6C/k+Db+bvByPyN7rrfpXF7sovfzc5SqO72AzubQpU+qmljcN3gMZHTyo9MguGGUmycTGhxhnQd5IicSbFnkGX2v8kH4yrCUWodq43Yhb4KpP+M0tjWmkXvv7eP9hjrqzq2txZo8ykSIVzwjRDlUK/diP+YEoF1FzTOKMrg0eZhxszQuXIIrfzqi7srMQpwRuxEkflXKnrPdN+qnL28fUf+g9o6dNVhJe7r6xzYy0nuOEjIR1ru9fYWMm6T5sNuaaoN0ng7CsvRWTOnVZqMP0xYj+yK3mksrJRNKhVJdjevzdGCXPo0xL0IerC/740z4tiUI07Ko5/1Mmu8E6wqLsFRqFMF4gKI6IOccsMhHmIDo71Xnx5cVh9Aqk4zLc/8on5F+6Au6XSjB/rbbVjJEddHQAueQs8A5lr7PutyRvBpu2vdjWOtIMdgg=="
 
 # production
-DOTENV_VAULT_PRODUCTION="V2Ubh9aLQArg3jAoPl+teCR43RK32DCIFgTMd6ipobMoQNEBlqP2lzJ0vBECidOJwCeoTa2Y8HXY4JEFq6fzKrMc8I9xYhQtwYy0ElEwVzCSi5KAnFVH0l+UC1eEgjWu2q5qcwaQIg6p4VNS9rZe9duA+DTPPZpHNcIfE65ll5vprauEQNQcRynruZZJl66bXfBuAxSXwE9xA3hVTgK1XwhmxHI4sSjG0vTEeN/dxOj55n18QUHfMiCZeyclessGResoxQZDO4cCVQsh3IWW1LRKA4OsWtE76MbrMLf5mltiGVRw7wmweLhUwB2HkXfxjuOw4PbhXFYrIze8cqaUQ5qW9DCn08AOzIW/uvGIlWZ8/53PBCYIekeks70ScocXlQNoOFqVnJNNc8/PVIvkb1KBKkoj3sBcuIPKVA+DZs8qFTMMKTLlideWHfPDvmYkvwNOw3TmK3mO1P9LWPIBdb23fyI8+2Rwu7ZzAqpdv6Hcqn9TzCGFS/GNrrus1bAKuZYbzqMhn3+WfmTqDmA6ZphoiHA/0FOIALmDRJeeai8kcmI65AiKzjiEmXp4NwXrovU2yEAfW/h1sEo4zKaTFrHIDEJBT4a6IDtkTBdw5pBOBC9SNR2L7uI1KncPsmGwMRY8gZzqffo/67cyTdpeSUYebJLN5TwXSqwUXzyBGkXaaY27MCC9wLp8y4ZjDIz77uzTZM6WLxCN8dypdMU9a5XNlBerFZnHVD5ih8C6t9nNE4mqakI/TBc9F8yrs8agHF03hv3ZfIxADo8sbZpe6hgSghI28g5sWe0teTMqPA5gs+G74npC205TnsZyRwXBLDaOK8wvvMBTUQF2ZEwaKYDrPA=="
+DOTENV_VAULT_PRODUCTION="+YyVGgsKuSuw/v4yOKTAuGFXQknLA2+mUdukojOkRzwPbiAus1xOCbNXVNKkSp4W6EwPDDguIYuujU9y8qDSIYcwlVL4fhemBUzBEA2+9M4uJhAeLF4x+9d8Jlkwe1g2s9y5x/l+NbM7l4yNohtFYutNDi6bm9f0ErzPvLXxIf/3zqCzlwUrTUGs9hGeTO1XbZ/UGwlFCRV5bWkkkDQpOdgWhapwPqt8cznDTdL2SuNQ37j0xWn0QKJDXInALpUAVNQmNy94Nhp/bUwBmyIuTV56woYMTlrQSQnxgS7vqc2zeafXBY/9r+4BB+mCGHKToGhymy96IwR+2v3t4tFhDFpR9offdGERj0Hk9md/eeRCBH91I+oIhTeREQfTMQM8l2YPmRDj/SNiBHpHlrvFCjXKshxuRzW/B9iZogyUa9r7kULMptstviwq2Yf4mmNiRpZ9l/EbudaF4/A24RscuB3ozX9/OyTnGs7b543AywZPektu9lcKk8yrGU0a57kEl4Hjm7MeX3hiN5Zo3C94VdDuWmM0v5EoOBYysAOrFYlMNmKOWFKtOK5QVUebCk5Fl5T137EHM1oAFX15OebitLaWBQ7NjMUhyC6ir5ufFpuRhJr8DD2ByHvQKwzKpFVuCVxT2rUR7F+VezZQBDyw65Pu2FOmKQYUEqKAIUJ1IbSuu3hbk5nkHY1Y7pcCBzsDo8kdIydZF4xw1VgkeCKGVNrUetP8ZHxlO9qWGfJFaESNmaSRYf3slsXvILZEGN/kIgyo+IWTpR19PI/ZnkS6YraeZAjF+/V/9sOf8rIi1KjY7syjTswKeTV+RfnLL9B+SmCgNQ1pLaE8p/dujNJztpOZa4C6xEjeOPLK05axaHkZA8y0eO8s4khe9W671o4p2Flx9akaEcnILSBvnA0TtiIySvUslLrmNy+UuzycIjHQ0Eiby/a7ZD4hOhzcseHO6tPfKCtGUK/j4cA/hk8g0BCjA++3+gqK6+EEHEnU+lYFCu8rd0t4g1rt3idUX9D6o89gLBb94Mk4bMpJbtzn3dzO0Pf4ujYUI9s4ROVfe/l7YBfV"
 
 # test
-DOTENV_VAULT_TEST="dvvA9U6xqfxxsXW+7cLIFDF1cfdf1FacenqptzxMReGTQdSR+F4f24+HvpMFGh8kDH/dKDR8zcckJgyXuCoqJpFupEoIFW9LVY/E/mwymJkJOvkEF0y3BnR1r6rK3iFrdcwaIjl+YCveUff5AWRT6hfKs10LMbp3KyrLs0NAapgc7SkarS2talR4F9YjCZSG1ssLjtqEpNyzaGK5vcUXl0kN6dy47wY9hysC1gzTX6TYaxqomonVeMB8fe/qHMG9ukCWGPGiAG3u8phtMKwW7ckkxSBYGyNWiOFVm3sK2jVJlUAgJBDtIjKAnD7+OCuHe7Nzn4vx85DqXAVURlv2HlnPoBUpjgbobRWt12yIWUsKPYlowGXYfqOnmiyBuAviUnwH59a8l+8pph/o57CURJBMkA5Tig7kI0dTrP+Ta5qsbsiKFwiygUOMnBukZpaJrwLzscGsbbrEhzgXBCC2X+D76jpyjpM03eNwQFozt/YD1P/9krF9AUAGPoALBEQkFxAkOyACasao5P8rb1ddKRW0ql61IKnvcSSW1/dJyKV+yfWjO9bgQorMMdPbFVuzniqeWOOFDCDWCgij1QUhqtcaOLLZCT5cub5dNxUb0xO0EElu5jP23vN4/hbK28cDnvbS9wA9vIwMpizPaCMuHfQh4d3RtkKkIv5CYpmbllJ9BaR5avYGobDVjlLuKBS0/qIXAQ6RuQJTkmkWWV7ooSuLfWZB4HeNu0sRRy3JUkMAN1K+3n5JtivXLTwMiGQZ8z0XgsoXND2yb1aX"
+DOTENV_VAULT_TEST="ndD67xmxFguLJ4zGyEIFIlXahix1aYpQbSPQ8ejTwBNBPmA3GGE0+xBPpJyxcK8ZGOoqbq1xzSL5j91+YWa2+Gb0YDDUFeXgUWwDkc4D/wxrH1bCJ2QqjR4v5C77VDRXAN9MBQSdOAt3trtCARBai7Ngq3wrDZvuoLXyufbw7HpvX1JDiHwsoqtGtaF1pDaEzlPSUnT+EN0Z1WLdZSnuP4Vmowb9mDcrDbUJzzjiPMreYmgldlktVZ3gWE07f5qGxLv/PjH/vp9UcfV6sjeMKPy2t2TKqKXQPa1+grr9RPt/xDiyou43MXFBi9G5rHG5MVi+4OVZyIQg++QpF3WPt4eCfP0Y8lhg5lr/0gUuz30I/ARu8O7MbrsRO7Sld/7zWlU6bc1crxlJK7Gg8KTI/HyJiKOyJYmAIjoxA/dglnWZtyoDjPNMiudaj5EFvIovo/GLare5nmQ+TKfi+/7Mx3o3Lw18DzlAFJ1LN1auxsGggti3wJFOH9BIoI78uJ/fe+yyVWPEMb8QaRPtTlezoa18gp7Zzk2UKMaxHLHTospT2V9BIwLBTw8wDb/LG3ASVoaiHWpATx0U+llhQgRJlPR7tWpQwGzhMMKS4l3C0P6LqFByZfLx81fQWNQJR5dkOryMU1cUsLPL1TS58q7IZhjGFOcBBUxM+vdJWqq/0VkdTj7ajRWJ6ex5sXVxyKwTeIvkZ7dZTgW+6a6+corUdywPFM9jGkqdGXvl8Dq+dLL4v3qGSDj3KBzXeECejQl5qun4SLRMrb/xFvpSuAhRLhmsMUGKIfDCy5EtymXpcd9Unx/ad0RH2CBtTkdrJ9zl7GEGURKeG0cWcuFU109w/s4x0LavLasPuVjmju9rF9aKR2I4nyeyQOhtUKoLDdbSH8VN65Qzf7W7dgzWYlvQodVtyAKWjaH0puRrb7l5Ohxja90gN2Ph6UP9vgF89ISA5W3xf+n6gbKsUCjF0JawgK9PSx1hkaLRiiDyoZQ="
 
diff --git a/ExerciseChecker/.idea/vcs.xml b/ExerciseChecker/.idea/vcs.xml
index d86e73b516141a07b5ba1f16f6f315749512bedc..8bd3f8944d87804b8c8764a48898c52d5ff89c64 100644
--- a/ExerciseChecker/.idea/vcs.xml
+++ b/ExerciseChecker/.idea/vcs.xml
@@ -2,6 +2,7 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/.idea/jetbrainsConfiguration" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/src/shared" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/src/sharedByClients" vcs="Git" />
   </component>
diff --git a/ExerciseChecker/package-lock.json b/ExerciseChecker/package-lock.json
index acc0da44e03bf02b5fb64052092034ecf060adcf..f0c599d7f46177b9bed3029b318983cf2e20bb0b 100644
--- a/ExerciseChecker/package-lock.json
+++ b/ExerciseChecker/package-lock.json
@@ -9,6 +9,7 @@
             "version": "3.4.0",
             "license": "AGPLv3",
             "dependencies": {
+                "@gitbeaker/rest": "^40.0.3",
                 "axios": "^1.6.5",
                 "boxen": "^5.1.2",
                 "chalk": "^4.1.2",
@@ -252,6 +253,45 @@
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
             }
         },
+        "node_modules/@gitbeaker/core": {
+            "version": "40.0.3",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-40.0.3.tgz",
+            "integrity": "sha512-MzeY4oCtoa9zmPIkQIdC2KU8cGmHIXwnAi0L6jjjouqjy6kcA4BydZf8W5Xsj27Rw5iiyhfj8YC1/O3CgrzvCQ==",
+            "dependencies": {
+                "@gitbeaker/requester-utils": "^40.0.3",
+                "qs": "^6.11.2",
+                "xcase": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=18.0.0"
+            }
+        },
+        "node_modules/@gitbeaker/requester-utils": {
+            "version": "40.0.3",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-40.0.3.tgz",
+            "integrity": "sha512-L8JpuMIsvXTHfu/2wXzkc5QyfQJSWg4XyEPStHq1ig5SAcbxxqbBoe8ed27eUXLah+PcGrPInMK4cCMxhQm41g==",
+            "dependencies": {
+                "picomatch-browser": "^2.2.6",
+                "qs": "^6.11.2",
+                "rate-limiter-flexible": "^4.0.0",
+                "xcase": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=18.0.0"
+            }
+        },
+        "node_modules/@gitbeaker/rest": {
+            "version": "40.0.3",
+            "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-40.0.3.tgz",
+            "integrity": "sha512-ihaA0GX3yCo4oUWbISkcjFMIw+WxDAC9L+bEYq2irz4wpv/0EpAU/0jKjggPzY4cGWL9VAyPhew77VeACv4YWw==",
+            "dependencies": {
+                "@gitbeaker/core": "^40.0.3",
+                "@gitbeaker/requester-utils": "^40.0.3"
+            },
+            "engines": {
+                "node": ">=18.0.0"
+            }
+        },
         "node_modules/@humanwhocodes/config-array": {
             "version": "0.11.14",
             "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -1365,6 +1405,24 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/call-bind": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+            "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+            "dependencies": {
+                "es-define-property": "^1.0.0",
+                "es-errors": "^1.3.0",
+                "function-bind": "^1.1.2",
+                "get-intrinsic": "^1.2.4",
+                "set-function-length": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/callsites": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -1690,6 +1748,22 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
+        "node_modules/define-data-property": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+            "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+            "dependencies": {
+                "es-define-property": "^1.0.0",
+                "es-errors": "^1.3.0",
+                "gopd": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/delayed-stream": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1835,6 +1909,25 @@
                 "is-arrayish": "^0.2.1"
             }
         },
+        "node_modules/es-define-property": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+            "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+            "dependencies": {
+                "get-intrinsic": "^1.2.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/es-errors": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
         "node_modules/escalade": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -2398,7 +2491,6 @@
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
             "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-            "dev": true,
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
             }
@@ -2429,6 +2521,24 @@
                 "node": "6.* || 8.* || >= 10.*"
             }
         },
+        "node_modules/get-intrinsic": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+            "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+            "dependencies": {
+                "es-errors": "^1.3.0",
+                "function-bind": "^1.1.2",
+                "has-proto": "^1.0.1",
+                "has-symbols": "^1.0.3",
+                "hasown": "^2.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/get-package-type": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -2538,6 +2648,17 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
+        "node_modules/gopd": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+            "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+            "dependencies": {
+                "get-intrinsic": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/graceful-fs": {
             "version": "4.2.11",
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2566,11 +2687,43 @@
                 "node": ">=8"
             }
         },
+        "node_modules/has-property-descriptors": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+            "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+            "dependencies": {
+                "es-define-property": "^1.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-proto": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+            "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-symbols": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/hasown": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
             "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
-            "dev": true,
             "dependencies": {
                 "function-bind": "^1.1.2"
             },
@@ -3363,6 +3516,14 @@
                 }
             }
         },
+        "node_modules/object-inspect": {
+            "version": "1.13.1",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+            "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/object-treeify": {
             "version": "1.1.33",
             "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz",
@@ -3610,6 +3771,17 @@
                 "url": "https://github.com/sponsors/jonschlinkert"
             }
         },
+        "node_modules/picomatch-browser": {
+            "version": "2.2.6",
+            "resolved": "https://registry.npmjs.org/picomatch-browser/-/picomatch-browser-2.2.6.tgz",
+            "integrity": "sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w==",
+            "engines": {
+                "node": ">=8.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/jonschlinkert"
+            }
+        },
         "node_modules/pkg": {
             "version": "5.8.1",
             "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz",
@@ -3768,6 +3940,20 @@
                 "node": ">=6"
             }
         },
+        "node_modules/qs": {
+            "version": "6.12.1",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
+            "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
+            "dependencies": {
+                "side-channel": "^1.0.6"
+            },
+            "engines": {
+                "node": ">=0.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/queue-microtask": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -3793,6 +3979,11 @@
             "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
             "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
         },
+        "node_modules/rate-limiter-flexible": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-4.0.1.tgz",
+            "integrity": "sha512-2/dGHpDFpeA0+755oUkW+EKyklqLS9lu0go9pDsbhqQjZcxfRyJ6LA4JI0+HAdZ2bemD/oOjUeZQB2lCZqXQfQ=="
+        },
         "node_modules/rc": {
             "version": "1.2.8",
             "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -4014,6 +4205,22 @@
                 "node": ">=10"
             }
         },
+        "node_modules/set-function-length": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+            "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+            "dependencies": {
+                "define-data-property": "^1.1.4",
+                "es-errors": "^1.3.0",
+                "function-bind": "^1.1.2",
+                "get-intrinsic": "^1.2.4",
+                "gopd": "^1.0.1",
+                "has-property-descriptors": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
         "node_modules/shebang-command": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4035,6 +4242,23 @@
                 "node": ">=8"
             }
         },
+        "node_modules/side-channel": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+            "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+            "dependencies": {
+                "call-bind": "^1.0.7",
+                "es-errors": "^1.3.0",
+                "get-intrinsic": "^1.2.4",
+                "object-inspect": "^1.13.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/signal-exit": {
             "version": "3.0.7",
             "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -4633,6 +4857,11 @@
             "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
             "dev": true
         },
+        "node_modules/xcase": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz",
+            "integrity": "sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw=="
+        },
         "node_modules/y18n": {
             "version": "5.0.8",
             "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/ExerciseChecker/package.json b/ExerciseChecker/package.json
index 191bed03146ddc20d34437f03dabbcbc9b963915..75a6b02e95c04d5ba12b99a08e5542d3f19b394c 100644
--- a/ExerciseChecker/package.json
+++ b/ExerciseChecker/package.json
@@ -1,16 +1,16 @@
 {
-    "name"           : "dojo_exercise_checker",
-    "description"    : "App that check an exercise of the Dojo project",
-    "version"        : "3.4.0",
-    "license"        : "AGPLv3",
-    "author"         : "Michaël Minelli <dojo@minelli.me>",
-    "main"           : "dist/app.js",
-    "bin"            : {
+    "name": "dojo_exercise_checker",
+    "description": "App that check an exercise of the Dojo project",
+    "version": "3.4.0",
+    "license": "AGPLv3",
+    "author": "Michaël Minelli <dojo@minelli.me>",
+    "main": "dist/app.js",
+    "bin": {
         "dirmanager": "./dist/app.js"
     },
-    "pkg"            : {
+    "pkg": {
         "scripts": [],
-        "assets" : [
+        "assets": [
             "node_modules/axios/dist/node/axios.cjs",
             ".env",
             "assets/**/*"
@@ -20,42 +20,43 @@
             "node18-linux-x64"
         ]
     },
-    "scripts"        : {
+    "scripts": {
         "dotenv:build": "npx dotenv-vault local build",
-        "lint"        : "npx eslint .",
-        "genversion"  : "npx genversion -s -e src/config/Version.ts",
-        "build"       : "npm run genversion; npx tsc",
-        "start:dev"   : "npm run genversion; npm run lint; npx ts-node src/app.ts",
-        "test"        : "echo \"Error: no test specified\" && exit 1"
+        "lint": "npx eslint .",
+        "genversion": "npx genversion -s -e src/config/Version.ts",
+        "build": "npm run genversion; npx tsc",
+        "start:dev": "npm run genversion; npm run lint; npx ts-node src/app.ts",
+        "test": "echo \"Error: no test specified\" && exit 1"
     },
-    "dependencies"   : {
-        "axios"               : "^1.6.5",
-        "boxen"               : "^5.1.2",
-        "chalk"               : "^4.1.2",
-        "dotenv"              : "^16.3.1",
-        "dotenv-expand"       : "^10.0.0",
-        "fs-extra"            : "^11.2.0",
-        "http-status-codes"   : "^2.3.0",
-        "json5"               : "^2.2.3",
-        "ora"                 : "^5.4.1",
-        "tar-stream"          : "^3.1.6",
-        "winston"             : "^3.11.0",
-        "yaml"                : "^2.3.4",
-        "zod"                 : "^3.22.4",
+    "dependencies": {
+        "@gitbeaker/rest": "^40.0.3",
+        "axios": "^1.6.5",
+        "boxen": "^5.1.2",
+        "chalk": "^4.1.2",
+        "dotenv": "^16.3.1",
+        "dotenv-expand": "^10.0.0",
+        "fs-extra": "^11.2.0",
+        "http-status-codes": "^2.3.0",
+        "json5": "^2.2.3",
+        "ora": "^5.4.1",
+        "tar-stream": "^3.1.6",
+        "winston": "^3.11.0",
+        "yaml": "^2.3.4",
+        "zod": "^3.22.4",
         "zod-validation-error": "^3.0.0"
     },
     "devDependencies": {
-        "@types/fs-extra"                 : "^11.0.4",
-        "@types/js-yaml"                  : "^4.0.9",
-        "@types/node"                     : "^18.19.8",
-        "@types/tar-stream"               : "^3.1.3",
+        "@types/fs-extra": "^11.0.4",
+        "@types/js-yaml": "^4.0.9",
+        "@types/node": "^18.19.8",
+        "@types/tar-stream": "^3.1.3",
         "@typescript-eslint/eslint-plugin": "^6.19.0",
-        "@typescript-eslint/parser"       : "^6.19.0",
-        "dotenv-vault"                    : "^1.25.0",
-        "genversion"                      : "^3.2.0",
-        "pkg"                             : "^5.8.1",
-        "tiny-typed-emitter"              : "^2.1.0",
-        "ts-node"                         : "^10.9.2",
-        "typescript"                      : "^5.3.3"
+        "@typescript-eslint/parser": "^6.19.0",
+        "dotenv-vault": "^1.25.0",
+        "genversion": "^3.2.0",
+        "pkg": "^5.8.1",
+        "tiny-typed-emitter": "^2.1.0",
+        "ts-node": "^10.9.2",
+        "typescript": "^5.3.3"
     }
 }
diff --git a/ExerciseChecker/src/app.ts b/ExerciseChecker/src/app.ts
index 2a519e885ef3ab66272ec4b9b4c6508c64d031f5..53c1e560bae18fa41d7e2fd5ca56871dd59a970b 100644
--- a/ExerciseChecker/src/app.ts
+++ b/ExerciseChecker/src/app.ts
@@ -28,6 +28,11 @@ import ExerciseResultsSanitizerAndValidator from './sharedByClients/helpers/Dojo
 import ExerciseAssignment                   from './sharedByClients/models/ExerciseAssignment';
 import ClientsSharedExerciseHelper          from './sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';
 import Icon                                 from './shared/types/Icon';
+import SharedConfig                         from './shared/config/SharedConfig';
+import assignment                           from './sharedByClients/models/Assignment';
+import SonarAnalyzer                        from './sharedByClients/helpers/Dojo/SonarAnalyzer';
+import AssignmentCheckerError               from './shared/types/Dojo/AssignmentCheckerError';
+import Exercise                             from './sharedByClients/models/Exercise';
 
 
 (async () => {
@@ -35,6 +40,7 @@ import Icon                                 from './shared/types/Icon';
 
     console.log(Styles.APP_NAME(`${ Config.appName } (version {{VERSION}})`));
 
+    let exercise: Exercise | undefined;
     let exerciseAssignment: ExerciseAssignment | undefined;
     let exerciseDockerCompose: ExerciseDockerCompose;
     let exerciseResultsValidation: ExerciseResultsSanitizerAndValidator;
@@ -49,9 +55,10 @@ import Icon                                 from './shared/types/Icon';
      */
     {
         console.log(Styles.INFO(`${ Icon.INFO }️Checking the exercise's assignment and his immutable files`));
+        exercise = await DojoBackendManager.getExercise();
         exerciseAssignment = await DojoBackendManager.getExerciseAssignment();
-        if ( !exerciseAssignment ) {
-            console.error(Styles.ERROR(`${ Icon.ERROR } Error while getting the exercise's assignment`));
+        if ( !exerciseAssignment || !exercise ) {
+            console.error(Styles.ERROR(`${ Icon.ERROR } Error while getting the exercise or exercise's assignment`));
             process.exit(ExerciseCheckerError.EXERCISE_ASSIGNMENT_GET_ERROR);
         }
 
@@ -64,6 +71,36 @@ import Icon                                 from './shared/types/Icon';
         haveResultsVolume = exerciseAssignment.assignmentFile.result.volume !== undefined;
     }
 
+    /*
+     //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 2:
+     - Run sonar analysis
+     */
+    console.log(SharedConfig.sonar);
+    console.log(exerciseAssignment.assignment.useSonar)
+    console.log(exercise);
+    if (SharedConfig.sonar.enabled && exerciseAssignment.assignment.useSonar) {
+        console.log(Styles.INFO(`${ Icon.INFO }Running Sonar analysis on the exercise`));
+
+        const buildSuccess = SonarAnalyzer.buildDocker()
+        if ( !buildSuccess ) {
+            console.error(Styles.ERROR(`${ Icon.ERROR } Error while building the Docker image`));
+            process.exit(ExerciseCheckerError.SONAR_DOCKER_ERROR);
+        }
+
+        if (SonarAnalyzer.mustRunBuild(exerciseAssignment.assignment.language, exerciseAssignment.assignmentFile.buildLine)) {
+            const buildSuccess = SonarAnalyzer.runBuildStep(exerciseAssignment.assignmentFile.buildLine!);
+            if ( !buildSuccess ) {
+                console.error(Styles.ERROR(`${ Icon.ERROR } Error while compiling exercise files`));
+                process.exit(ExerciseCheckerError.SONAR_BUILD_ERROR);
+            }
+        }
+
+        const runSuccess = SonarAnalyzer.runAnalysis(exercise.sonarKey, exerciseAssignment.assignment.language, exerciseAssignment.assignmentFile.buildLine);
+        if ( !runSuccess ) {
+            console.error(Styles.ERROR(`${ Icon.ERROR } Sonar gate failed`));
+            process.exit(ExerciseCheckerError.SONAR_GATE_FAILED);
+        }
+    }
 
     /*
      //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 2:
diff --git a/ExerciseChecker/src/managers/DojoBackendManager.ts b/ExerciseChecker/src/managers/DojoBackendManager.ts
index 102069ead041b6877029dad772edb7fb81a45211..171a3f55b48456c76610e60dc08059b73ffa67be 100644
--- a/ExerciseChecker/src/managers/DojoBackendManager.ts
+++ b/ExerciseChecker/src/managers/DojoBackendManager.ts
@@ -6,6 +6,7 @@ import Config              from '../config/Config';
 import ExerciseResultsFile from '../shared/types/Dojo/ExerciseResultsFile';
 import ApiRoute            from '../sharedByClients/types/Dojo/ApiRoute';
 import { IFileDirStat }    from '../shared/helpers/recursiveFilesStats/RecursiveFilesStats';
+import Exercise            from '../sharedByClients/models/Exercise';
 
 
 class DojoBackendManager {
@@ -13,6 +14,14 @@ class DojoBackendManager {
         return `${ ClientsSharedConfig.apiURL }${ route }`;
     }
 
+    public async getExercise(): Promise<Exercise | undefined> {
+        try {
+            return (await axios.get<DojoBackendResponse<Exercise>>(this.getApiUrl(ApiRoute.EXERCISE_GET).replace('{{id}}', Config.exercise.id))).data.data;
+        } catch ( error ) {
+            return undefined;
+        }
+    }
+
     public async getExerciseAssignment(): Promise<ExerciseAssignment | undefined> {
         try {
             return (await axios.get<DojoBackendResponse<ExerciseAssignment>>(this.getApiUrl(ApiRoute.EXERCISE_ASSIGNMENT).replace('{{id}}', Config.exercise.id))).data.data;
diff --git a/ExerciseChecker/src/shared b/ExerciseChecker/src/shared
index 89f3579ca9009f793742170928d808ab4c35d931..bd29fe76fdb1b124e3fe2f23e995a2b3b70694a7 160000
--- a/ExerciseChecker/src/shared
+++ b/ExerciseChecker/src/shared
@@ -1 +1 @@
-Subproject commit 89f3579ca9009f793742170928d808ab4c35d931
+Subproject commit bd29fe76fdb1b124e3fe2f23e995a2b3b70694a7
diff --git a/ExerciseChecker/src/sharedByClients b/ExerciseChecker/src/sharedByClients
index 098c6d20f6ed84240c086b979b56afd598fdfea4..41b3d88544eb46171acf36b4fd61332c33db5bf8 160000
--- a/ExerciseChecker/src/sharedByClients
+++ b/ExerciseChecker/src/sharedByClients
@@ -1 +1 @@
-Subproject commit 098c6d20f6ed84240c086b979b56afd598fdfea4
+Subproject commit 41b3d88544eb46171acf36b4fd61332c33db5bf8
diff --git a/sonar/Dockerfile b/sonar/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..cc163bca27eb2fff8d20660d501ccae283facbf3
--- /dev/null
+++ b/sonar/Dockerfile
@@ -0,0 +1,28 @@
+FROM gcc:14
+
+ARG SONAR_HOST_URL=https://isc-sonar.edu.hesge.ch
+
+RUN apt update && apt install -y curl unzip build-essential make g++ clang && apt clean
+
+# Download sonar tools
+RUN mkdir -p /sonar && \
+    curl -sSLo sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip && \
+    unzip -o sonar-scanner.zip -d /sonar && \
+    mv /sonar/sonar-scanner-5.0.1.3006-linux/* /sonar/ && \
+    ln -s /sonar/bin/sonar-scanner /usr/local/bin/sonar-scanner && \
+    curl --insecure -sSLo build-wrapper-linux-x86.zip  "$SONAR_HOST_URL/static/cpp/build-wrapper-linux-x86.zip" && \
+    unzip -o build-wrapper-linux-x86.zip -d /tmp && \
+    mv /tmp/build-wrapper-linux-x86/* /usr/local/bin/ && \
+    rm build-wrapper-linux-x86.zip sonar-scanner.zip
+
+
+COPY ./cacerts /tmp/cacerts
+ENV SONAR_SCANNER_OPTS="-Djavax.net.ssl.trustStore=/tmp/cacerts"
+RUN mkdir -p /usr/src && \
+  useradd -m sonar && \
+  chown sonar:sonar /usr/src && \
+  chmod 744 /tmp/cacerts
+
+USER sonar
+WORKDIR /usr/src
+
diff --git a/sonar/cacerts b/sonar/cacerts
new file mode 100644
index 0000000000000000000000000000000000000000..3259ae13ad7391f25b7f093d12f71fb5da333272
Binary files /dev/null and b/sonar/cacerts differ