Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-71366

403 Forbidden on CSS stylesheet from same directory using HTML Publisher

    • Icon: Improvement Improvement
    • Resolution: Fixed
    • Icon: Major Major
    • core
    • Jenkins 2.346.1
      HTML Publisher 1.31
    • 2.417, 2.414.2

      The generated report.html is not able to load its stylesheet report.css served from the same directory using the HTML Publisher plugin.

      Following the suggestions from https://www.jenkins.io/doc/book/security/configuring-content-security-policy/ we were able to successfully use inline CSS after modifying the server CSP by adding 'unsafe-inline' to style-src in the Content-Security-Policy header. However we are looking for a way to apply the CSS from a static stylesheet report.css served from the same directory so that we may use the more secure default CSP setting (which can be seen in the server response below).

      The report.html includes the stylesheet in the html head:

      <head>
        <title>The Title</title>
        <link rel="stylesheet" href="report.css">
      </head>
      

      The report.html and report.css files are in the same directory, and both load when requested directly. However when report.html is requested, its content loads, but looking in the browser console, report.css is requested but the server responds with a 403 Forbidden.

      Here are the headers in sequence:

      Request:

      GET /path/to/report/report.html HTTP/1.1
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Cache-Control: no-cache
      Connection: keep-alive
      Cookie: jenkins-timestamper=system; jenkins-timestamper-local=true; screenResolution=3840x2160; jenkins-timestamper-offset=14400000; JSESSIONID....
      Host: jenkins.example.com
      Pragma: no-cache
      Referer: https://jenkins.example.com/path/to/report/
      Sec-Fetch-Dest: iframe
      Sec-Fetch-Mode: navigate
      Sec-Fetch-Site: same-origin
      Upgrade-Insecure-Requests: 1
      User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
      sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"
      sec-ch-ua-mobile: ?0
      sec-ch-ua-platform: "Linux"
      

      Response:

      HTTP/1.1 200 OK
      Server: nginx
      Date: Tue, 30 May 2023 15:16:26 GMT
      Content-Type: text/html;charset=utf-8
      Content-Length: 5042
      Connection: keep-alive
      X-Content-Type-Options: nosniff
      Content-Security-Policy: sandbox; default-src 'none'; img-src 'self'; style-src 'self';
      X-WebKit-CSP: sandbox; default-src 'none'; img-src 'self'; style-src 'self';
      X-Content-Security-Policy: sandbox; default-src 'none'; img-src 'self'; style-src 'self';
      Last-Modified: Tue, 30 May 2023 15:12:01 GMT
      Expires: Tue, 30 May 2023 15:12:01 GMT
      Accept-Ranges: bytes
      Content-Encoding: gzip
      

      Request:

      GET /path/to/report/report.css HTTP/1.1
      Accept: text/css,*/*;q=0.1
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Cache-Control: no-cache
      Connection: keep-alive
      Host: jenkins.example.com
      Pragma: no-cache
      Sec-Fetch-Dest: style
      Sec-Fetch-Mode: no-cors
      Sec-Fetch-Site: cross-site
      User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
      sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"
      sec-ch-ua-mobile: ?0
      sec-ch-ua-platform: "Linux"
      

      Response:

      HTTP/1.1 403 Forbidden
      Server: nginx
      Date: Tue, 30 May 2023 15:16:26 GMT
      Content-Type: text/html;charset=utf-8
      Transfer-Encoding: chunked
      Connection: keep-alive
      X-Content-Type-Options: nosniff
      Set-Cookie: JSESSIONID.e6e60840=node055rgmmxlx15qojhhfap9tow0347.node0; Path=/; Secure; HttpOnly
      Expires: Thu, 01 Jan 1970 00:00:00 GMT
      X-Hudson: 1.395
      X-Jenkins: 2.346.1
      X-Jenkins-Session: 7514db71
      Content-Encoding: gzip
      

      I am puzzled by why the client has Sec-Fetch-Site: cross-site in its request headers when requesting the CSS file. It is obviously being requested from the same site and even the same directory as the HTML file it is linked from. However I don't see anything in the prior server response that would cause the client to issue that. This same issue has been seen on both Linux and Mac running Chrome.

          [JENKINS-71366] 403 Forbidden on CSS stylesheet from same directory using HTML Publisher

          Reuben added a comment -

          This is a very annoying bug. Is it something to do with the report being hosted in an iframe?

          Reuben added a comment - This is a very annoying bug. Is it something to do with the report being hosted in an iframe?

          Matt added a comment - - edited

          I had the same question, but was able to reproduce it without an iframe: https://bugs.chromium.org/p/chromium/issues/detail?id=1450340

          My current hypothesis is that it is a browser bug. The browser is submitting the header Sec-Fetch-Site: cross-site for the CSS file when it should be Sec-Fetch-Site: same-origin.

          But I believe what I am trying to do is so basic that I am hoping that I'm missing something that can be readily resolved, without unnecessarily compromising web site security.

          Matt added a comment - - edited I had the same question, but was able to reproduce it without an iframe: https://bugs.chromium.org/p/chromium/issues/detail?id=1450340 My current hypothesis is that it is a browser bug. The browser is submitting the header Sec-Fetch-Site: cross-site for the CSS file when it should be Sec-Fetch-Site: same-origin . But I believe what I am trying to do is so basic that I am hoping that I'm missing something that can be readily resolved, without unnecessarily compromising web site security.

          Reuben added a comment -

          Not sure it's a browser bug - it looks more like a badly-chosen default CSP value by Jenkins. 

          It's the sandbox part that's doing it. Since it doesn't include the allow-same-origin qualifier  - and here I quote the spec - "If this token is not used, the resource is treated as being from a special origin that always fails the same-origin policy".

          If you use Chrome DevTools to change the CSP to add allow-same-origin after the word sandbox it all works fine. I'm not sure why always-failing the same-origin policy would ever be desirable but its not really my domain. It must solve a problem for someone somewhere.

          Reuben added a comment - Not sure it's a browser bug - it looks more like a badly-chosen default CSP value by Jenkins.  It's the sandbox part that's doing it. Since it doesn't include the allow-same-origin qualifier  - and here I quote the spec - "If this token is not used, the resource is treated as being from a special origin that always fails the same-origin policy ". If you use Chrome DevTools to change the CSP to add allow-same-origin  after the word sandbox it all works fine. I'm not sure why always-failing the same-origin policy would ever be desirable but its not really my domain. It must solve a problem for someone somewhere.

          Matt added a comment -

          I agree, it does appear to be a badly chosen CSP. In my test nginx.conf I changed it to have:

          add_header Content-Security-Policy "sandbox allow-same-origin; default-src 'none'; img-src 'self'; style-src 'self';"; 

          and it works as expected: the CSS file is requested with header 

          Sec-Fetch-Site: same-origin

          If the htmlpublisher-plugin is unable to modify the Jenkins CSP, may I get your help in escalating this to the appropriate board?

          In the meantime, what is the recommended way to make this change to the Jenkins Content-Security-Policy header that can survive reboots? I've read https://www.jenkins.io/doc/book/security/configuring-content-security-policy/ and some stackoverflow answers that were not as explicit as could be. Plus this seems like a fundamental use case for which clear documentation would be useful for this plugin.

          Matt added a comment - I agree, it does appear to be a badly chosen CSP. In my test nginx.conf I changed it to have: add_header Content-Security-Policy "sandbox allow-same-origin; default -src 'none' ; img-src 'self' ; style-src 'self' ;" ; and it works as expected: the CSS file is requested with header  Sec-Fetch-Site: same-origin If the htmlpublisher-plugin is unable to modify the Jenkins CSP, may I get your help in escalating this to the appropriate board? In the meantime, what is the recommended way to make this change to the Jenkins Content-Security-Policy header that can survive reboots? I've read https://www.jenkins.io/doc/book/security/configuring-content-security-policy/ and some stackoverflow answers that were not as explicit as could be. Plus this seems like a fundamental use case for which clear documentation would be useful for this plugin.

          Reuben added a comment -

          Am I right in thinking that, since 'sandbox' on its own ensures that the same-origin test always fails, then surely the 'img-src: self' and 'style-src: self' declarations do not have the intended effect (or indeed any effect) and we can consider that a bug? We're not really changing the intent of the CSP, we're wanting it to be what it probably should have been all along. Can that really be right though? I can't help but think surely someone would have noticed before now.

          If the above is true then I guess we submit a one-line PR for this line here :

          https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/DirectoryBrowserSupport.java#L872

          Re persisting the change, I'm afraid I don't know, this is all rather outside my normal domain. It certainly was a little alarming seeing all the Stack Overflow activity dedicated to turning off CSP completely! Which is itself another good argument for changing it... the existing default CSP is clearly making many sites less secure, not more.

          Reuben added a comment - Am I right in thinking that, since 'sandbox' on its own ensures that the same-origin test always fails, then surely the 'img-src: self' and 'style-src: self' declarations do not have the intended effect (or indeed any effect) and we can consider that a bug? We're not really changing the intent of the CSP, we're wanting it to be what it probably should have been all along. Can that really be right though? I can't help but think surely someone would have noticed before now. If the above is true then I guess we submit a one-line PR for this line here : https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/DirectoryBrowserSupport.java#L872 Re persisting the change, I'm afraid I don't know, this is all rather outside my normal domain. It certainly was a little alarming seeing all the Stack Overflow activity dedicated to turning off CSP completely! Which is itself another good argument for changing it... the existing default CSP is clearly making many sites less secure, not more.

          Daniel Beck added a comment -

          what is the recommended way to make this change to the Jenkins Content-Security-Policy header that can survive reboots?

          See https://www.jenkins.io/doc/book/managing/system-properties/#usage for how to set Java system properties. I filed https://github.com/jenkins-infra/jenkins.io/pull/6544 to hopefully make this clearer.

          Since this is a system property that can be changed (with the desired effect) while Jenkins is running, alternatively put the script in https://www.jenkins.io/doc/book/managing/groovy-hook-scripts/#post-initialization-script-init-hook

          Daniel Beck added a comment - what is the recommended way to make this change to the Jenkins Content-Security-Policy header that can survive reboots? See https://www.jenkins.io/doc/book/managing/system-properties/#usage for how to set Java system properties. I filed https://github.com/jenkins-infra/jenkins.io/pull/6544 to hopefully make this clearer. Since this is a system property that can be changed (with the desired effect) while Jenkins is running, alternatively put the script in https://www.jenkins.io/doc/book/managing/groovy-hook-scripts/#post-initialization-script-init-hook

          Daniel Beck added a comment -

          since 'sandbox' on its own ensures that the same-origin test always fails, then surely the 'img-src: self' and 'style-src: self' declarations do not have the intended effect (or indeed any effect)

          Without these directives, the browser would not even request images or stylesheets, as you can easily test by running

          System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "sandbox; default-src 'none';") 

          (These specifically relax the much more restrictive default-src, which is what actually prohibits JS and some other stuff.)

          With these directives, the browser requests them, but considers them to be on a different origin. So there is an effect, even a desirable one, it just goes a bit too far for some reverse proxy configurations.

          Daniel Beck added a comment - since 'sandbox' on its own ensures that the same-origin test always fails, then surely the 'img-src: self' and 'style-src: self' declarations do not have the intended effect (or indeed any effect) Without these directives, the browser would not even request images or stylesheets, as you can easily test by running System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "sandbox; default-src 'none';") (These specifically relax the much more restrictive default-src , which is what actually prohibits JS and some other stuff.) With these directives, the browser requests them, but considers them to be on a different origin. So there is an effect, even a desirable one, it just goes a bit too far for some reverse proxy configurations.

            r2b2_nz Richard Bywater
            mattp Matt
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: