diff --git a/docs/backend/config.md b/docs/backend/config.md
index 30abd98e25f2617589c2102b05bc081545988743..9f3277e87eee5e33c28c32a38e6b2a2949464c42 100644
--- a/docs/backend/config.md
+++ b/docs/backend/config.md
@@ -11,7 +11,7 @@ Konfiguration über:
 Der Pfad zum [subato2-data](persistence.md#subato2-data) Verzeichnis kann über `$SUBATO2_DATA_DIR` konfiguriert werden.
 
 ## Hostname
-Wenn mit dem Backend über einen Proxy kommuniziert wird, sollte `$SUBATO2_PUBLIC_HOSTNAME` auf den öffentlichen Hostnamen gesetzt werden. Andernfalls ist das nicht notwendig.
+Wenn mit dem Backend über einen Proxy kommuniziert wird, sollte `$SUBATO2_PUBLIC_HOSTNAME` auf den öffentlichen Hostnamen gesetzt werden. Andernfalls ist das nicht notwendig. Wenn sich die Ports auch unterscheiden, kann der öffentliche Port so konfiguriert werden: `SUBATO2_PUBLIC_HOSTNAME=api.subato.cs.hs-rm.de:443`
 
 Grund dafür ist, dass relative URLs zu Bildern und anderen Medien in der Aufgabenstellung zu importierten Aufgaben auf absolute URLs umgeschrieben werden, sobald die Aufgabenstellung vom Backend abgerufen wird. Standardmäßig wird für die absolute URL der Hostname verwendet, der bei der HTTP Anfrage verwendet wurde. Wird das Backend nun über eine interne Adresse durch den Proxy angesprochen, können die absoluten URLs von außen ggf. nicht erreicht werden.   
 
diff --git a/subato-web/package.json b/subato-web/package.json
index b2afc5bfbd39368ba6cd410ea702e9b26f44296f..b1bda6e95eea832c5572436daa4ec440b1e4008c 100755
--- a/subato-web/package.json
+++ b/subato-web/package.json
@@ -1,6 +1,6 @@
 {
     "name": "subato-web",
-    "version": "2.1.5",
+    "version": "2.1.6",
     "license": "MIT",
     "scripts": {
         "ng": "ng",
diff --git a/subato-web/src/app/assessment/pages/submission/submission.page.component.html b/subato-web/src/app/assessment/pages/submission/submission.page.component.html
index 459ac8caeac6439960ee78039062ebb96128b648..cc6551c43458bb2b5c7919866a7e6ded5eb6993b 100644
--- a/subato-web/src/app/assessment/pages/submission/submission.page.component.html
+++ b/subato-web/src/app/assessment/pages/submission/submission.page.component.html
@@ -73,7 +73,8 @@
                             Abgabefrist abgegeben
                         </nb-alert>
 
-                        <ng-container *ngIf="vm.solutionState.solution">
+                        <ng-container
+                            *ngIf="vm.showRegularResultsTab || vm.showAcceptanceResultsTab">
                             <div class="row">
                                 <div class="col-12">
                                     <nb-card>
diff --git a/subato-web/src/app/assessment/pages/submission/submission.page.store.ts b/subato-web/src/app/assessment/pages/submission/submission.page.store.ts
index 81dfa31167dfe3ea5e35758f4e6394d54ad0526a..4151fc1b661e59a894784e49406970f9f55f97ad 100644
--- a/subato-web/src/app/assessment/pages/submission/submission.page.store.ts
+++ b/subato-web/src/app/assessment/pages/submission/submission.page.store.ts
@@ -510,13 +510,16 @@ export class SubmissionPageStore extends BaseComponentStore<SubmissionPageState>
     );
 
     readonly updateSolution$ = this.effect(() =>
-        this.currentTaskInstance$.pipe(
+        combineLatest(this.submission$, this.currentTaskInstance$).pipe(
+            filter(([submission, ti]) => ti != null && submission != null),
             distinctUntilChanged(
-                (a, b) => a == b || (a != null && a.equals(b))
+                ([submissionA, taskInstanceA], [submissionB, taskInstanceB]) =>
+                    (taskInstanceA == taskInstanceB ||
+                        taskInstanceA.equals(taskInstanceB)) &&
+                    (submissionA == submissionB ||
+                        submissionA.id == submissionB.id)
             ),
-            withLatestFrom(this.submission$),
-            filter(([ti, submission]) => ti != null && submission != null),
-            map(([ti, submission]) => ({
+            map(([submission, ti]) => ({
                 taskId: ti.task.id,
                 submissionId: submission.id,
                 valid: true,
diff --git a/subato-web/src/environments/shared.ts b/subato-web/src/environments/shared.ts
index 0eb246794d09f57b736058abea207cbe132a2a01..44d54c1c5af7338030e096ea8206b4e4eeea60c3 100644
--- a/subato-web/src/environments/shared.ts
+++ b/subato-web/src/environments/shared.ts
@@ -2,7 +2,7 @@ let docsUrl = 'http://docs.subato-test2.local.cs.hs-rm.de';
 let manualUrl = `${docsUrl}/docs`;
 
 export const shared = {
-    version: '2.1.5',
+    version: '2.1.6',
     docsUrl: docsUrl,
     manualUrl: manualUrl,
     stefDocsUrl: `${docsUrl}/stef`,
diff --git a/subato/build.gradle b/subato/build.gradle
index 0b7e9ca0932f5211d227b9c8941dd0d2a4d98c41..353f9046ce6baa73d3d044996301feae96650e0f 100755
--- a/subato/build.gradle
+++ b/subato/build.gradle
@@ -19,7 +19,7 @@ plugins {
 }
 
 group = 'de.hsrm.sls'
-version = '2.1.5-SNAPSHOT'
+version = '2.1.6-SNAPSHOT'
 
 repositories {
     mavenCentral()
diff --git a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/StreamFilter.java b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/StreamFilter.java
index 5c876ee42a00377be7f01272275e49845eaa45bc..8e1db106e436b9cb239460205184164747d569d6 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/StreamFilter.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/StreamFilter.java
@@ -6,6 +6,7 @@ import de.hsrm.sls.subato.auth.stream.token.StreamTokenManager;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,6 +25,18 @@ import org.springframework.web.filter.OncePerRequestFilter;
 @Component
 public class StreamFilter extends OncePerRequestFilter {
 
+    private class RequestWithoutQueryString extends HttpServletRequestWrapper {
+
+        public RequestWithoutQueryString(HttpServletRequest request) {
+            super(request);
+        }
+
+        @Override
+        public String getQueryString() {
+            return "";
+        }
+    }
+
     @Autowired
     private StreamTokenManager tokenManager;
 
@@ -36,8 +49,12 @@ public class StreamFilter extends OncePerRequestFilter {
         try {
             tokenManager.activate(request);
         } catch (InvalidStreamTokenException ex) {
+            // store current requested url for auto redirect after successful login
+            // but remove the invalid token from the query string to prevent infinite loops
+            var modifiedRequest = new RequestWithoutQueryString(request);
+            requestCache.saveRequest(modifiedRequest, response);
+
             // redirect to login
-            requestCache.saveRequest(request, response);
             String redirectUrl =
                 request.getContextPath() + SecurityConfig.OAUTH_LOGIN_URL;
             response.sendRedirect(redirectUrl);
diff --git a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenContext.java b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenContext.java
index 254cb93bab68d5570d2a4133663a2061c3af86bf..ba1a2dff9c748513f08bc087916f2f26d46e9aa3 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenContext.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenContext.java
@@ -29,11 +29,13 @@ class StreamTokenContext {
      */
     private final String signedUrl;
 
-    public StreamTokenContext(String signedUrl, String username, String originalUrl, String token) {
+
+    public StreamTokenContext(String signedUrl, String username, String originalUrl, String token,
+        LocalDateTime issuedAt) {
         this.signedUrl = signedUrl;
         this.username = username;
         this.originalUrl = originalUrl;
-        this.issuedAt = LocalDateTime.now();
+        this.issuedAt = issuedAt;
         this.token = token;
     }
 
@@ -63,6 +65,16 @@ class StreamTokenContext {
         return now.isAfter(expirationDate);
     }
 
+    public StreamTokenContext refresh() {
+        return new StreamTokenContext(
+            signedUrl,
+            username,
+            originalUrl,
+            token,
+            LocalDateTime.now()
+        );
+    }
+
     @Override
     public String toString() {
         return "StreamTokenContext{" +
diff --git a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenGenerator.java b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenGenerator.java
index b386d9e8862431bc2e8fced765dfb69eb180b6a3..e16e3c2cea4f8f57a59dab72fdde547f739d1730 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenGenerator.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenGenerator.java
@@ -2,6 +2,7 @@ package de.hsrm.sls.subato.auth.stream.token;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.time.LocalDateTime;
 import java.util.UUID;
 import org.springframework.stereotype.Service;
 
@@ -32,7 +33,13 @@ class StreamTokenGenerator {
 
             URI updatedUri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), newQuery,
                 uri.getFragment());
-            return new StreamTokenContext(updatedUri.toString(), username, url, token);
+            return new StreamTokenContext(
+                updatedUri.toString(),
+                username,
+                url,
+                token,
+                LocalDateTime.now()
+            );
         } catch (URISyntaxException e) {
             throw new RuntimeException("Generating token failed because the url is invalid", e);
         }
diff --git a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenManager.java b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenManager.java
index e33baaa24844643d962dec1f7f0eced4fd77e6c7..1dd674183ef5e2d1636700d245efc62085879a84 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenManager.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/auth/stream/token/StreamTokenManager.java
@@ -16,8 +16,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Service;
 
 /**
- * Coordinates requests for signing urls, manages the state of signed tokens and activates
- * tokens on request.
+ * Coordinates requests for signing urls, manages the state of signed tokens and activates tokens on
+ * request.
  */
 @Service
 public class StreamTokenManager {
@@ -37,24 +37,23 @@ public class StreamTokenManager {
     StreamTokenConfig streamTokenConfig;
 
     /**
-     * Lookup map required for token activation. If the token was extracted from the query param,
-     * we need to get the context to check expiration, principal, ...
+     * Lookup map required for token activation. If the token was extracted from the query param, we
+     * need to get the context to check expiration, principal, ...
      */
     private Map<String, StreamTokenContext> tokenContextMap = new HashMap<>();
 
     /**
-     * Reverse lookup map required for signing. If we already signed a url for the same user,
-     * we don't need to sign again.
+     * Reverse lookup map required for signing. If we already signed a url for the same user, we
+     * don't need to sign again.
      */
     private Map<ReverseStreamTokenKey, String> reverseTokenMap = new HashMap<>();
 
 
     /**
-     * Signs the given url by generating a stream token (with respect to the current user),
-     * stores the token in-memory and
-     * returns the signed url for redirection to a client.
+     * Signs the given url by generating a stream token (with respect to the current user), stores
+     * the token in-memory and returns the signed url for redirection to a client.
      *
-     * @param url may be ABSOLUTE or RELATIVE (with respect to the backend root)
+     * @param url     may be ABSOLUTE or RELATIVE (with respect to the backend root)
      * @param request for generating the absolute url
      * @return the ABSOLUTE url with the appended stream token
      */
@@ -80,7 +79,10 @@ public class StreamTokenManager {
         if (reverseTokenMap.containsKey(key)) {
             var token = reverseTokenMap.get(key);
             context = tokenContextMap.get(token);
-            logger.trace("URL already signed for principal %s".formatted(principal.getUsername()));
+            context = context.refresh();
+            tokenContextMap.put(context.getToken(), context);
+            logger.trace(
+                "Refreshed signed URL for principal %s".formatted(principal.getUsername()));
         } else {
             context = tokenGenerator.generate(parseResult.relativeUrl(), principal.getUsername());
             tokenContextMap.put(context.getToken(), context);
@@ -101,8 +103,8 @@ public class StreamTokenManager {
 
 
     /**
-     * Extract the token from the request and activate it by setting the current user to the principal
-     * associated with the token.
+     * Extract the token from the request and activate it by setting the current user to the
+     * principal associated with the token.
      */
     public synchronized void activate(HttpServletRequest request) {
         String relativeUrl = request.getRequestURI();
@@ -113,11 +115,6 @@ public class StreamTokenManager {
         }
         logger.trace("URL: %s".formatted(relativeUrl));
 
-        if (principalService.getPrincipal() != null) {
-            logger.trace("Already provided JWT");
-            return;
-        }
-
         var token = parseTokenFromUrl(relativeUrl);
         if (token == null) {
             logger.trace("No token provided");
diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/HtmlUrlRewriter.java b/subato/src/main/java/de/hsrm/sls/subato/shared/HtmlUrlRewriter.java
index e8d480c54a7949e0ffb22ff3931dc95c0e5274db..051773162d42c0567ae908b2104cacae25a1a2d2 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/shared/HtmlUrlRewriter.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/shared/HtmlUrlRewriter.java
@@ -18,16 +18,18 @@ public class HtmlUrlRewriter {
 
     /**
      * Replaces relative urls in the html content
-     * @param html to search for relative urls
-     * @param replaceFunction taking the base url (e.g. http://localhost:8081)
-     *                        and the relative url (e.g. images/star.png)
-     * @param request for deriving the base url
+     *
+     * @param html            to search for relative urls
+     * @param replaceFunction taking the base url (e.g. http://localhost:8081) and the relative url
+     *                        (e.g. images/star.png)
+     * @param request         for deriving the base url
      * @return the transformed html
      */
     public String replaceRelativeUrlsInHtml(String html,
         BiFunction<String, String, String> replaceFunction,
         HttpServletRequest request) {
-        var baseUrl = HttpServletRequestHelper.getBaseUrl(request, subatoConfig);
+        var baseUrls = HttpServletRequestHelper.getBaseUrls(request, subatoConfig);
+        var baseUrl = baseUrls.get(0);
 
         return replaceUrlsInHtml(html,
             url -> UriHelper.isRelative(url) ? replaceFunction.apply(baseUrl, url) : url
@@ -35,10 +37,12 @@ public class HtmlUrlRewriter {
     }
 
     /**
-     * replaces urls in the given html (only those, who appear in the src-attribute,
-     * e.g. in img-tags)
-     * @param html to search for urls
-     * @param replacerFunction function that receives the original url and should return the new url
+     * replaces urls in the given html (only those, who appear in the src-attribute, e.g. in
+     * img-tags)
+     *
+     * @param html             to search for urls
+     * @param replacerFunction function that receives the original url and should return the new
+     *                         url
      * @return the transformed html
      */
     public String replaceUrlsInHtml(String html, Function<String, String> replacerFunction) {
diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/helper/HttpServletRequestHelper.java b/subato/src/main/java/de/hsrm/sls/subato/shared/helper/HttpServletRequestHelper.java
index 575640e1ab21acb83237e94ef3e6d97521514ff4..5b5d67115fcd89f6d49b5393a2f1f199d10e9e19 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/shared/helper/HttpServletRequestHelper.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/shared/helper/HttpServletRequestHelper.java
@@ -3,17 +3,51 @@ package de.hsrm.sls.subato.shared.helper;
 import com.google.common.base.Strings;
 import de.hsrm.sls.subato.shared.config.SubatoConfig;
 import jakarta.servlet.http.HttpServletRequest;
+import java.util.List;
 
 public class HttpServletRequestHelper {
 
-    public static String getBaseUrl(HttpServletRequest request, SubatoConfig subatoConfig) {
-        var hostname =
-            Strings.isNullOrEmpty(subatoConfig.getPublicHostname()) ? request.getServerName()
-                : subatoConfig.getPublicHostname();
+    public static List<String> getBaseUrls(HttpServletRequest request, SubatoConfig subatoConfig) {
+        var hostname = getHostname(request, subatoConfig);
+        var port = getPort(request, subatoConfig);
 
-        return ""
-            + request.getScheme() + "://"
-            + hostname
-            + ":" + request.getServerPort();
+        if (port == 80 || port == 443) {
+            return List.of(
+                createUrl(request.getScheme(), hostname, null),
+                createUrl(request.getScheme(), hostname, port)
+            );
+        } else {
+            return List.of(
+                createUrl(request.getScheme(), hostname, port)
+            );
+        }
+    }
+
+    private static String createUrl(String scheme, String hostname, Integer port) {
+        var sb = new StringBuilder();
+        sb.append(scheme + "://");
+        sb.append(hostname);
+        if (port != null) {
+            sb.append(":" + port);
+        }
+        sb.append("/");
+        return sb.toString();
+    }
+
+    private static String getHostname(HttpServletRequest request, SubatoConfig subatoConfig) {
+        if (Strings.isNullOrEmpty(subatoConfig.getPublicHostname())) {
+            return request.getServerName();
+        }
+
+        return subatoConfig.getPublicHostname().split(":")[0];
+    }
+
+    private static int getPort(HttpServletRequest request, SubatoConfig subatoConfig) {
+        if (Strings.isNullOrEmpty(subatoConfig.getPublicHostname())) {
+            return request.getServerPort();
+        }
+
+        var splitted = subatoConfig.getPublicHostname().split(":");
+        return splitted.length >= 2 ? Integer.parseInt(splitted[1]) : request.getServerPort();
     }
 }
diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/helper/UriHelper.java b/subato/src/main/java/de/hsrm/sls/subato/shared/helper/UriHelper.java
index fd4090b1c0e28ecc78b2a8342953543b8c137899..3b7fca5c55e3dc615e48584f60bc2053c0aad234 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/shared/helper/UriHelper.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/shared/helper/UriHelper.java
@@ -61,17 +61,20 @@ public class UriHelper {
 
     public static UrlParseResult parseUrl(String url, HttpServletRequest request,
         SubatoConfig subatoConfig) {
-        var baseUrl = HttpServletRequestHelper.getBaseUrl(request, subatoConfig);
+        var baseUrls = HttpServletRequestHelper.getBaseUrls(request, subatoConfig);
 
         if (UriHelper.isRelative(url)) {
+            var baseUrl = baseUrls.get(0);
             return new UrlParseResult(baseUrl, null, url);
         } else {
-            if (url.startsWith(baseUrl)) {
-                var relativeUrl = url.replace(baseUrl, "");
-                return new UrlParseResult(baseUrl, url, relativeUrl);
-            } else {
-                return null;
+            for (var baseUrl : baseUrls) {
+                if (url.startsWith(baseUrl)) {
+                    var relativeUrl = url.replace(baseUrl, "");
+                    return new UrlParseResult(baseUrl, url, relativeUrl);
+                }
             }
+
+            return null;
         }
     }
 
diff --git a/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java b/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java
index 67155838cf892e2fc6a7ceac6ecf37b38ff42c58..b5057c3fe8911c2740b8d0fb46d3ca0f9f538a8d 100644
--- a/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java
+++ b/subato/src/main/java/de/hsrm/sls/subato/shared/swagger/SwaggerConfiguration.java
@@ -30,7 +30,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 @Configuration
 public class SwaggerConfiguration implements WebMvcConfigurer {
 
-    private final String API_VERSION = "2.1.5";
+    private final String API_VERSION = "2.1.6";
 
     @Value("${keycloak.auth-server-url}")
     private String keycloakUrl;
diff --git a/subato/src/main/resources/console-appender.xml b/subato/src/main/resources/console-appender.xml
index d49a375bd2b367ee796aab84b77dbdfcb3276a11..ae96783341a33b248e9d2c55c8555eef6f559a37 100644
--- a/subato/src/main/resources/console-appender.xml
+++ b/subato/src/main/resources/console-appender.xml
@@ -2,7 +2,7 @@
 <included>
   <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
     <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
-      <level>${CONSOLE_LOG_THRESHOLD}</level>
+      <level>ALL</level>
     </filter>
     <filter class="de.hsrm.sls.subato.shared.LogFilter">
       <!-- ... additional filter configuration ... -->
diff --git a/subato/src/test/java/de/hsrm/sls/subato/courses/course/GetCourseDescriptionTest.java b/subato/src/test/java/de/hsrm/sls/subato/courses/course/GetCourseDescriptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..14432a34be7401a6d15ff3e30f623cc122d42ca1
--- /dev/null
+++ b/subato/src/test/java/de/hsrm/sls/subato/courses/course/GetCourseDescriptionTest.java
@@ -0,0 +1,58 @@
+package de.hsrm.sls.subato.courses.course;
+
+import static de.hsrm.sls.subato.utils.MockMvcHelper.getJson;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import de.hsrm.sls.subato.Tags;
+import de.hsrm.sls.subato.courses.course.repository.CourseRepository;
+import org.junit.Assert;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithUserDetails;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.web.servlet.MockMvc;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+@Tag(Tags.INTEGRATION)
+public class GetCourseDescriptionTest {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Autowired
+    private CourseRepository courseRepository;
+
+    @Test
+    @DirtiesContext
+    @WithUserDetails("U_C5A")
+    public void shouldSignImageInDescription() throws Exception {
+        var course = courseRepository.findById(5L).get();
+        course.setDescription(
+            "<img src=\"http://localhost:80/course_file/80/download\">");
+        courseRepository.save(course);
+
+        var response = this.mockMvc.perform(getJson("/course/5/description"))
+            .andDo(print()).andExpect(status().isOk()).andReturn();
+        var description = response.getResponse().getContentAsString();
+        Assert.assertTrue(description.contains("/80/download?t="));
+    }
+
+    @Test
+    @DirtiesContext
+    @WithUserDetails("U_C5A")
+    public void shouldNotSignImageInDescription() throws Exception {
+        var course = courseRepository.findById(5L).get();
+        course.setDescription("<img src=\"http://localhost:81/course_file/80/download\">");
+        courseRepository.save(course);
+
+        var response = this.mockMvc.perform(getJson("/course/5/description"))
+            .andDo(print()).andExpect(status().isOk()).andReturn();
+        var description = response.getResponse().getContentAsString();
+        Assert.assertFalse(description.contains("/80/download?t="));
+    }
+}
diff --git a/subato/src/test/resources/data.sql b/subato/src/test/resources/data.sql
index 231e6e2080bf70e08a488a49ea56b056091862b1..203d56cd92320537fcd5eda9b4fe12f4d2d1077b 100644
--- a/subato/src/test/resources/data.sql
+++ b/subato/src/test/resources/data.sql
@@ -1,16 +1,16 @@
-INSERT INTO users (id, login_name, mail, active) VALUES (1, 'U_C1A', 'dummy@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (2, 'U_C1T', 'dummy2@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (3, 'U_C1L', 'dummy3@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (4, 'U_STAFF', 'staff@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (5, 'U_STUD', 'student@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (6, 'U_STUD_C1P', 'studentC1@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (7, 'U_C4A', 'adminC4@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (8, 'U_C4L', 'lectC4@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (9, 'U_C4T', 'tutC4@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (10, 'U_STUD_C4P', 'studC4@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (11, 'U_SA', 'sudo@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (12, 'U_STUD_C5P', 'studC5@local.cs.hs-rm.de', TRUE);
-INSERT INTO users (id, login_name, mail, active) VALUES (13, 'U_C5A', 'adminC5@local.cs.hs-rm.de', TRUE);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (1, 'U_C1A', 'dummy@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (2, 'U_C1T', 'dummy2@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (3, 'U_C1L', 'dummy3@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (4, 'U_STAFF', 'staff@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (5, 'U_STUD', 'student@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (6, 'U_STUD_C1P', 'studentC1@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (7, 'U_C4A', 'adminC4@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (8, 'U_C4L', 'lectC4@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (9, 'U_C4T', 'tutC4@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (10, 'U_STUD_C4P', 'studC4@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (11, 'U_SA', 'sudo@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (12, 'U_STUD_C5P', 'studC5@local.cs.hs-rm.de', TRUE, FALSE, 0);
+INSERT INTO users (id, login_name, mail, active, consented_to_collection, consent_change_count) VALUES (13, 'U_C5A', 'adminC5@local.cs.hs-rm.de', TRUE, FALSE, 0);
 
 INSERT INTO user_roles (user_id, role_id) VALUES (4, 1);
 INSERT INTO user_roles (user_id, role_id) VALUES (5, 2);
@@ -30,11 +30,11 @@ INSERT INTO term (id, name, source_id, lectures_start_at, lectures_end_at, aor_i
 INSERT INTO term (id, name, source_id, lectures_start_at, lectures_end_at, aor_id) VALUES (4, 'WS2526', 47, '2025-10-17', '2026-02-03', 986159136);
 INSERT INTO term (id, name, source_id, lectures_start_at, lectures_end_at, aor_id) VALUES (5, 'WS2627', 48, '2026-10-17', '2027-02-03', 986159137);
 
-INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails) VALUES (1, 'Objektorientierte Softwareentwicklung', 'OOSE', 'Bla', 'Hallo', 0, 0, 1, 0);
-INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails) VALUES (2, 'Objektorientierte Softwareentwicklung', 'OOSE', 'Für AoR Import', 'Hallo', 0, 0, 1, 0);
-INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails) VALUES (3, 'Objektorientierung 3 Electric Boogaloo', 'OOSE3', 'Bla', 'Hallo', 1, 0, 3, 0);
-INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails) VALUES (4, 'Objektorientierung 4 Electric Threegaloo', 'OOSE4', 'Bla', 'Hallo', 2, 0, 4, 0);
-INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails) VALUES (5, 'Objektorientierung 5 Electric Fourgaloo', 'OOSE5', 'Bla', 'Hallo', 3, 0, 5, 0);
+INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails, allow_joining_units) VALUES (1, 'Objektorientierte Softwareentwicklung', 'OOSE', 'Bla', 'Hallo', 0, 0, 1, 0, FALSE);
+INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails, allow_joining_units) VALUES (2, 'Objektorientierte Softwareentwicklung', 'OOSE', 'Für AoR Import', 'Hallo', 0, 0, 1, 0, FALSE);
+INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails, allow_joining_units) VALUES (3, 'Objektorientierung 3 Electric Boogaloo', 'OOSE3', 'Bla', 'Hallo', 1, 0, 3, 0, FALSE);
+INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails, allow_joining_units) VALUES (4, 'Objektorientierung 4 Electric Threegaloo', 'OOSE4', 'Bla', 'Hallo', 2, 0, 4, 0, FALSE);
+INSERT INTO course (id, name, short_name, description, motd, visibility, submit_policy, term_id, show_emails, allow_joining_units) VALUES (5, 'Objektorientierung 5 Electric Fourgaloo', 'OOSE5', 'Bla', 'Hallo', 3, 0, 5, 0, FALSE);
 
 INSERT INTO course_staff_memberships(user_id, course_id, group_id) VALUES(1, 1, 0);
 INSERT INTO course_staff_memberships(user_id, course_id, group_id) VALUES(2, 1, 2);
@@ -86,7 +86,7 @@ INSERT INTO task_instance (id, exercise_id, task_id, reachable_points, order_num
 
 INSERT INTO submission (id, submitted_by_id, exercise_id, checked_by_id, passed) VALUES (1, 1, 1, null, null);
 INSERT INTO submission (id, submitted_by_id, exercise_id, checked_by_id, passed) VALUES (2, 1, 2, null, null);
-INSERT INTO eval_result (id, runtime_output, compiling, compilation_output, allocations, frees, tests_run, test_failures, test_errors, evaluator_eid) VALUES (16, '', false, e'Hier steht Kompilierausgabe', 0, 0, 0, 0, 0, 4);
+INSERT INTO eval_result (id, runtime_output, compiling, compilation_output, allocations, frees, tests_run, test_failures, test_errors, evaluator_eid, failed) VALUES (16, '', false, e'Hier steht Kompilierausgabe', 0, 0, 0, 0, 0, 4, FALSE);
 INSERT INTO solution (id, task_id, exercise_id, submitted_by_id, submitted_at, source_id, eval_result_id, submission_id, delayed, attempts_exceeded, submitted_with) VALUES (1, 11, 1, 1, '2023-03-14 23:16:12.243953', null, 16, 1, false, false, 0);
 
 -- required since Spring 6 / Hibernate 6 --