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

[Question v2] Gatsby script loader in external file #8025

Closed
outofthisworld opened this issue Sep 10, 2018 · 6 comments · Fixed by #8043
Closed

[Question v2] Gatsby script loader in external file #8025

outofthisworld opened this issue Sep 10, 2018 · 6 comments · Fixed by #8043
Labels
type: question or discussion Issue discussing or asking a question about Gatsby

Comments

@outofthisworld
Copy link
Contributor

outofthisworld commented Sep 10, 2018

Is there a way that I can have Gatsby load<script id="gatsby-script-loader"> from an external script file (without manually moving it) ? I would like to set content-security-policy headers to not allow inline scripts for security and since this is the only inline script generated for most of my pages I thought it was worth asking. Thanks!

Update: This script also doesn't show up during onPreRenderHTML, is there a way to obtain a reference to the component or is it injected by webpack into the page output ?

@kakadiadarpan
Copy link
Contributor

@pieh any inputs on this?

@outofthisworld
Copy link
Contributor Author

outofthisworld commented Sep 11, 2018

For clarification there is two ways I can think of that would work for me. One is that the script is loaded externally. I think this would mean the script loader would have to be generated for each page. The other idea I had was in onPreRenderHTML dynamically generate and insert a meta content-security-policy tag using the hashes of any inline scripts and styles, something like this:

export const onPreRenderHTML = ({
    getHeadComponents,
    getPreBodyComponents,
    getPostBodyComponents,
    replaceHeadComponents }) => {

    const allowed = {
        scriptHashes: [],
        styleHashes: []
    }

    getHeadComponents()
        .concat(getPreBodyComponents())
        .concat(getPostBodyComponents())
        .forEach((component) => {
            if (component.type === 'style') {
                const [originalMatch, strippedMatch] = renderToString(component).match(/^<style .*>(.*)<\/style>$/mi)
                const contentHash = `'sha256-${crypto.createHash('sha256').update(strippedMatch).digest('base64')}'`;
                component['props']['data-style-hash'] = contentHash;
                allowed.styleHashes[allowed.styleHashes.length] = contentHash;
            } else if (component.type === 'script') {
                //If it doesn't reference an external script
                if (component.href) return;

                const [originalMatch, strippedMatch] = renderToString(component).match(/^<script .*>(.*)<\/script>$/mi)
                const contentHash = `'sha256-${crypto.createHash('sha256').update(strippedMatch).digest('base64')}'`;
                component['props']['data-script-hash'] = contentHash;
                allowed.scriptHashes[allowed.styleHashes.length] = contentHash;
            }
        })
    
    const meta =  <meta http-equiv="Content-Security-Policy" 
    content={`default-src 'self'; connect-src 'self';  script-src 'self' ${allowed.scriptHashes.join(' ').trim()}; style-src 'self' ${allowed.styleHashes.join(' ').trim()}; img-src 'self'`}/>

    replaceHeadComponents([meta].concat(getHeadComponents()))
}

This would work if gatsby-script-loader was actually passed as a component to onPreRenderHTML

@pieh
Copy link
Contributor

pieh commented Sep 11, 2018

I think it would be valid change to move onPreRenderHTML to after we added gatsby-script-loader, so you could implement this on your site (or even create plugin for it) - would You like to create PR for it @outofthisworld ?

@pieh
Copy link
Contributor

pieh commented Sep 11, 2018

Here's the file that handles that - https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/static-entry.js - right now you don't have access for it, because we run onPreRenderHTML before we actually create entry for it in postBodyComponents

@pieh pieh added the type: question or discussion Issue discussing or asking a question about Gatsby label Sep 11, 2018
@outofthisworld
Copy link
Contributor Author

@pieh sure thing, would it be valid to move the gatsby-chunk-mapping script in the same PR?

@pieh
Copy link
Contributor

pieh commented Sep 11, 2018

yeah, I think moving

apiRunner(`onPreRenderHTML`, {
    getHeadComponents,
    replaceHeadComponents,
    getPreBodyComponents,
    replacePreBodyComponents,
    getPostBodyComponents,
    replacePostBodyComponents,
  })

to right before

const html = `<!DOCTYPE html>${renderToStaticMarkup(
    <Html
      {...bodyProps}
      headComponents={headComponents}
      htmlAttributes={htmlAttributes}
      bodyAttributes={bodyAttributes}
      preBodyComponents={preBodyComponents}
      postBodyComponents={postBodyComponents}
      body={bodyHtml}
      path={pagePath}
    />
  )}`

makes sense

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question or discussion Issue discussing or asking a question about Gatsby
3 participants