Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Content-Security-Policy Trusted-Types to fix error Failed to set the 'innerHTML' property on 'Element' #1566

Closed
rebeccan opened this issue Mar 21, 2022 · 3 comments
Labels
duplicate This issue or pull request already exists

Comments

@rebeccan
Copy link

Problem:
I have setup a Content-Security-Policy that requires trusted types, also for my swagger-ui.html.

My CSP header looks like
Content-Security-Policy: ...; require-trusted-types-for 'script'; trusted-types default;

It broke my Swagger, when expanding an endpoint on the UI. The UI shows the message 😱 Could not render n, see the console.. My console says tons of messages like TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment. at ve (swagger-ui-bundle.js:2:739971) at ou (swagger-ui-bundle.js:2:806017) at Cs (swagger-ui-bundle.js:2:826320) at Os (swagger-ui-bundle.js:2:826203) at ks (swagger-ui-bundle.js:2:826070) at Ss (swagger-ui-bundle.js:2:825933) at vs (swagger-ui-bundle.js:2:822920) at swagger-ui-bundle.js:2:772592 at t.unstable_runWithPriority (swagger-ui-bundle.js:2:849631) at Wo (swagger-ui-bundle.js:2:772369).

Possible Solutions:
InnerHTML is not safe to use because of XSS, that is why my CSP breaks things. This is fixable by adding a javascript function that defines the missing default policy. I just don't know how to add it to the swagger-ui bundle.

  • Is there a way that I can add my custom javascript function to swagger bundle?
  • Can you add support for trusted types, e.g. by adding a swagger policy that I can add to trusted-types?
@bnasslahsen
Copy link
Contributor

@rebeccan,

Already answered here #1492 and here #1456.

@bnasslahsen bnasslahsen added the duplicate This issue or pull request already exists label Mar 21, 2022
@rebeccan
Copy link
Author

rebeccan commented Mar 22, 2022

Hi, thanks for pointing me into the right direction! I got it working by extending SwaggerIndexPageTransformer class. It is not really beautiful, but it works and I can add my own javascript now.

I would like to share my Solution:

Extend SwaggerIndexPageTransformer and add the script tag for the javascript in transform method.

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.springdoc.core.SwaggerUiConfigParameters;
import org.springdoc.core.SwaggerUiConfigProperties;
import org.springdoc.core.SwaggerUiOAuthProperties;
import org.springdoc.webmvc.ui.SwaggerIndexPageTransformer;
import org.springdoc.webmvc.ui.SwaggerWelcomeCommon;
import org.springframework.core.io.Resource;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.resource.ResourceTransformerChain;
import org.springframework.web.servlet.resource.TransformedResource;

/**
 * Overrides SwaggerIndexPageTransformer to fix CSP trusted types errors
 */
public class MySwaggerIndexTransformerAddPolicy extends SwaggerIndexPageTransformer {

    public MySwaggerIndexTransformerAddPolicy(SwaggerUiConfigProperties swaggerUiConfig,
        SwaggerUiOAuthProperties swaggerUiOAuthProperties, SwaggerUiConfigParameters swaggerUiConfigParameters,
        SwaggerWelcomeCommon swaggerWelcomeCommon) {
        super(swaggerUiConfig, swaggerUiOAuthProperties, swaggerUiConfigParameters, swaggerWelcomeCommon);
    }

    /**
     * Adds CSP trusted-types default policy script to Swagger index.html
     */
    @Override
    public Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain)
        throws IOException {

        Resource resourceTransformedByParent = super.transform(request, resource, transformerChain);

        final AntPathMatcher antPathMatcher = new AntPathMatcher();
        boolean isIndexHtml = antPathMatcher.match("**/swagger-ui/**/index.html", resource.getURL().toString());

        if (isIndexHtml) {
            String html = readFullyAsString(resourceTransformedByParent.getInputStream());
            String htmlWithCustomJsScript = html.replace("<body>",
                "<body>\r\n<script src=\"/swaggerTrustedTypesPolicy.js\" charset=\"UTF-8\"> </script>");
            return new TransformedResource(resource, htmlWithCustomJsScript.getBytes());
        }
        return resourceTransformedByParent;
    }
}

Define Bean of the above class in a MySwaggerConfig class:

import org.springdoc.core.SwaggerUiConfigParameters;
import org.springdoc.core.SwaggerUiConfigProperties;
import org.springdoc.core.SwaggerUiOAuthProperties;
import org.springdoc.webmvc.ui.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import static org.springdoc.core.Constants.SPRINGDOC_SWAGGER_UI_ENABLED;

@Lazy(false)
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class MySwaggerConfig {

    /**
     * Overwrites the indexPageTransformer Bean coming from SwaggerConfig.
     */
    @Bean
    @Primary
    @Lazy(false)
    SwaggerIndexTransformer myIndexPageTransformer(SwaggerUiConfigProperties swaggerUiConfig,
        SwaggerUiOAuthProperties swaggerUiOAuthProperties, SwaggerUiConfigParameters swaggerUiConfigParameters,
        SwaggerWelcomeCommon swaggerWelcomeCommon) {
        return new MySwaggerIndexTransformerAddPolicy(swaggerUiConfig, swaggerUiOAuthProperties,
            swaggerUiConfigParameters, swaggerWelcomeCommon);
    }
}

Reference the MySwaggerConfig class inside OpenApiConfig class:

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@OpenAPIDefinition
@Import({MySwaggerConfig.class })
public class OpenApiConfig {

}

@bnasslahsen
Copy link
Contributor

@rebeccan,

Thank you for your sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants