Relaxed Content-Security-Policy for Go Code Coverage Reports

February 25, 2022

There is a conflict between my strict Content-Security-Policy and the CSS and JavaScript embedded in the HTML code coverage reports generated by go cover.

I tested a couple of methods of overriding the base Content-Security-Policy, without success:

  1. Add a relaxed <meta http-equiv='Content-Security-Policy' content='...'> element.
  2. Embed the script and style as data: URLs.

(Aside: I’m glad browsers don’t allow these workarounds, because they would be potential security holes).

In any case, the my solution was to relax the policy for a specific location via the Apache config:

#
# Relax style-src and script-src content security policies for content
# in the "/coverage-reports" directory so that the HTML coverage reports
# generated by `go cover` work as expected.
#
# Specifically the relaxed constraints allow:
#
# 1. The inline `<script>` element at the end of the generated HTML.
# 2. `style='display: none'` attributes on hidden elements.
#
# Notes:
#
# * You *have* to use `Header set` rather than `Header append` to
#   replace rather than append to the existing header.
# * Ideally we'd use `style-src-elem` and `script-src-elem`, but neither
#   are currently supported by Firefox.
#
<Location /coverage-reports>
  Header set "Content-Security-Policy" "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'"
</Location>

 

The situation could be improved by making the following changes to the go cover HTML report source code:

  1. Replace style="display: none" attribute on elements with class=hide.
  2. Add .hide { display: none } to the inline <style> element.
  3. Hash the inner content of the inline <style> element.
  4. Hash the inner content of the inline <script> element.
  5. Add a <meta http-equiv ...> element to the header, using the hashes from the previous two steps.

Example:

<meta
  http-equiv="content-security-policy"
  content="default-src 'self'; script-src 'self' 'sha256-a43KCehRqYcFBGPJxgYD6a15e6CRFwVvwuDAe8rGbkM='; style-src 'self' 'sha256-8OTC92xYkW7CWPJGhRvqCR0U1CR6L8PhhpRGGxgW4Ts='"
/>

 

Bonus: These reports would automatically set a reasonable policy even in the absense of a more restrictive server policy.

Counter-argument to this post: These coverage reports are meant to be simple and only used locally. More elaborate or more secure coverage reports should be generated by a separate tool rather than complicating the default one.