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

Page with CSP breaks puppeteer #1229

Closed
aslushnikov opened this issue Oct 31, 2017 · 32 comments
Closed

Page with CSP breaks puppeteer #1229

aslushnikov opened this issue Oct 31, 2017 · 32 comments

Comments

@aslushnikov
Copy link
Contributor

A page with CSP will break some of puppeteer methods, such as page.addScriptTag. At the very least, we should fail gracefully.

It would be ideal to have a way to disable CSP altogether.

Reported in: #1091

@rakeshpai
Copy link

rakeshpai commented Dec 4, 2017

+1 for ability to disable CSP.

Use case: I'm using puppeteer as a post-build crawler to generate a static dump of my SPA. When crawling, I'd like to inline critical CSS, and add a CSP directive for the CSS text hash. While I'm at it, I want to change the API server from localhost:port to api.example.com. However, if my page already had a CSP meta tag in it (which is good to have during development so I can catch CSP violations early), puppeteer starts getting funky. Would love it if I can disable CSP altogether when crawling.

@sradu
Copy link

sradu commented Dec 20, 2017

+1 for ability to disable CSP.

Thank you.

@yujiosaka
Copy link

yujiosaka commented Dec 21, 2017

In order to disable CSP, it's not a complete solution but adding nonce-<base64-value> to script tag will relax the security blocks. It only works for the site where the CSP headers or meta tags have the nonce part. It doesn't work for other sites, but in this way we don't have to change the default behavior or disable security blocks.

I'm working on that idea, but it will be probably difficult to completely disable CSP with current Chrome DevTool Protocol because I don't find APIs to intercept/modify response headers to do that yet.

@niieani
Copy link

niieani commented Jan 13, 2018

Here's my workaround:

  import fetch from 'node-fetch'

  const requestInterceptor = async (request) => {
    const url = request.url()
    const requestHeaders = request.headers()
    const acceptHeader = requestHeaders.accept || ''
    if (url.includes('some-website-with-csp.com') && (acceptHeader.includes('text/html'))) {
      const cookiesList = await page.cookies(url)
      const cookies = cookiesList.map(cookie => `${cookie.name}=${cookie.value}`).join('; ')
      delete requestHeaders['x-devtools-emulate-network-conditions-client-id']
      if (requestHeaders.Cookie) {
        requestHeaders.cookie = requestHeaders.Cookie
        delete requestHeaders.Cookie
      }
      const theseHeaders = Object.assign({'cookie': cookies}, requestHeaders, {'accept-language': 'en-US,en'})

      const init = {
        body: request.postData(),
        headers: theseHeaders,
        method: request.method(),
        follow: 20,
      }
      const result = await fetch(
        url,
        init,
      )
      const resultHeaders = {}
      result.headers.forEach((value, name) => {
        if (name.toLowerCase() !== 'content-security-policy') {
          resultHeaders[name] = value
        } else {
          console.log('CSP', `omitting CSP`, {originalCSP: value})
        }
      })
      const buffer = await result.buffer()
      await request.respond({
        body: buffer,
        resultHeaders,
        status: result.status,
      })
    } else {
      request.continue();
    }
  }

  await page.setRequestInterception(true)
  page.on('request', requestInterceptor)

Related: #599

@marcelmattsson
Copy link

@niieani, I'm getting requestStory undefined. is it something I'm missing or where does that variable come from?

@niieani
Copy link

niieani commented Jan 16, 2018

@marcelmattsson apologies, that was just my logger I forgot to remove before posting (I use storyboard for logging). Corrected the snippet above.

@marcelmattsson
Copy link

haha no worries, guess I'm doing something else wrong then since I'm still getting the EvalError even though I also see the console log telling me its omitting CSP.

@niieani
Copy link

niieani commented Jan 16, 2018

@marcelmattsson perhaps some TypeScript leftovers were causing it (I'm assuming you're not using it)? I've removed all the types from my snippet now (I think so at least).

@marcelmattsson
Copy link

Was never CSP in the headers at all... was meta tags with CSP who was the culprit.

@XBeg9
Copy link

XBeg9 commented Jan 22, 2018

+1 CSP breaks my flow. :( no workarounds as for now.

@niieani
Copy link

niieani commented Jan 22, 2018

Hi @XBeg9, doesn't my workaround work for you?

aslushnikov added a commit that referenced this issue Apr 3, 2018
This patch adds a test to fixate page.waitForFunction behavior
for pages with CSP.

References #1229.
aslushnikov added a commit to aslushnikov/puppeteer that referenced this issue Apr 4, 2018
This roll includes:
- https://crrev.com/547982 - v8 roll that includes [fixed
  Runtime.callFunctionOn](https://chromium.googlesource.com/v8/v8/+/1637818671c66333e3a3cf1b96a7326ba163f8b5) method

The upstream fix makes it possible to run frame.waitFor* functions on
pages with strict CSP.

References puppeteer#1229.
aslushnikov added a commit that referenced this issue Apr 4, 2018
This roll includes:
- https://crrev.com/547982 - v8 roll that includes [fixed
  Runtime.callFunctionOn](https://chromium.googlesource.com/v8/v8/+/1637818671c66333e3a3cf1b96a7326ba163f8b5) method

The upstream fix makes it possible to run frame.waitFor* functions on
pages with strict CSP.

References #1229.
aslushnikov added a commit to aslushnikov/puppeteer that referenced this issue Apr 6, 2018
This patch adds a test that Page.evaluateOnNewDocument works
with CSP: there's been some concerns on the bugtracker before.

References puppeteer#1229
aslushnikov added a commit that referenced this issue Apr 6, 2018
This patch adds a test that Page.evaluateOnNewDocument works
with CSP: there's been some concerns on the bugtracker before.

References #1229
aslushnikov added a commit to aslushnikov/puppeteer that referenced this issue Apr 6, 2018
This patch teaches Page.addScriptTag and Page.addStyleTag to throw
an error when blocked by CSP.

References puppeteer#1229.
aslushnikov added a commit that referenced this issue Apr 6, 2018
This patch teaches Page.addScriptTag and Page.addStyleTag to throw
an error when blocked by CSP.

References #1229.
aslushnikov added a commit to aslushnikov/puppeteer that referenced this issue Apr 6, 2018
This roll includes:
- https://crrev.com/548598 - DevTools: implement Page.setBypassCSP method
- https://crrev.com/548690 - DevTools: introduce Page.navigatedWithinDocument event

References puppeteer#1229, puppeteer#257.
aslushnikov added a commit that referenced this issue Apr 6, 2018
This roll includes:
- https://crrev.com/548598 - DevTools: implement Page.setBypassCSP method
- https://crrev.com/548690 - DevTools: introduce Page.navigatedWithinDocument event

References #1229, #257.
aslushnikov added a commit to aslushnikov/puppeteer that referenced this issue Apr 6, 2018
This patch introduces `page.setBypassCSP` method that allows clients
to ignore Content-Security-Policy for a given page.

Fixes puppeteer#1229.
aslushnikov added a commit that referenced this issue Apr 6, 2018
This patch introduces `page.setBypassCSP` method that allows clients
to ignore Content-Security-Policy for a given page.

Fixes #1229.
@garris
Copy link

garris commented Apr 10, 2018

Thanks @niieani — your solution inspired this simplified approach for a backstopjs usecase garris/BackstopJS@67801c2

@aslushnikov looking forward to #2324! 🎁

@niieani
Copy link

niieani commented Apr 10, 2018

Great to hear it was useful @garris! Also looking forward to #2324 :)

@o0101
Copy link

o0101 commented Jan 16, 2022

Another workaround (without removing CSP from the entire page, which depending on security requirements of your use may leave the site too wide open) is to parse the CSP script-src directive, and pick a URL that is allowed.

Then in your injected script tag create a fake URL from the origin allowed by CSP, for example, if CSP script-src looks like:

script-src 'self' https://*.example.com

then inject a script tag with the following src:

<script src=https://random343489.example.com/my-fake-path/my-script-001.js></script>

then create a fake "virtual proxy" to intercept requests for:

https://random343489.example.com/

and keep an internal map of

{
  ['my-fake-path/my-script-001.js']: {
    headers: { ... },
    responseBody: '...'
 }
}

and serve those headers and body in response to the request you intercepted from your injected script tag.

@aespinoza96
Copy link

By setting the flag page.setBypassCSP to true work for me.
#2324

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.