{"id":52079,"date":"2021-11-21T15:07:46","date_gmt":"2021-11-21T15:07:46","guid":{"rendered":"https:\/\/barrycarlyon.co.uk\/wordpress\/?p=52079"},"modified":"2021-11-26T15:27:05","modified_gmt":"2021-11-26T15:27:05","slug":"twitch-extensions-part-6-dev-environment-updates-content-security-policy","status":"publish","type":"post","link":"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/11\/21\/twitch-extensions-part-6-dev-environment-updates-content-security-policy\/","title":{"rendered":"Twitch Extensions Part 6 \u2013 Dev Environment Updates &#8211; Content Security Policy!"},"content":{"rendered":"\n<p>In <a href=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\" data-type=\"URL\" data-id=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\">part 5<\/a> we wrote about a suitable testing platform for building your extensions on, essentially we create a static content server, that mimics the Twitch CDN for testing with.<\/p>\n\n\n\n<p>Twitch <a rel=\"noreferrer noopener\" href=\"https:\/\/discuss.dev.twitch.tv\/t\/new-extensions-policy-for-content-security-policy-csp-directives-and-timeline-for-enforcement\/33695\/1\" data-type=\"URL\" data-id=\"https:\/\/discuss.dev.twitch.tv\/t\/new-extensions-policy-for-content-security-policy-csp-directives-and-timeline-for-enforcement\/33695\/1\" target=\"_blank\">Announced on the Forums<\/a> that they are revising the CSP (Content Security Policy) that extensions use to protect and control what can be loaded. I wrote about this in the <a rel=\"noreferrer noopener\" href=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/10\/01\/twitchs-extension-csp-is-changing\/\" data-type=\"URL\" data-id=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/10\/01\/twitchs-extension-csp-is-changing\/\" target=\"_blank\">previous blog post<\/a>.<\/p>\n\n\n\n<p>I&#8217;m currently waiting on a response from Twitch (via the forums) about any other changes to the CSP, but for now, you can test the changes today!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Even is CSP<\/h2>\n\n\n\n<p>First lets do a quick explanation of what CSP, CSP is Content Security Policy, a browser technology to help control what a given Website can load and what browser functions are allowed.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>The HTTP&nbsp;<strong><code>Content-Security-Policy<\/code><\/strong>&nbsp;response header allows web site administrators to control resources the user agent is allowed to load for a given page. With a few exceptions, policies mostly involve specifying server origins and script endpoints. This helps guard against cross-site scripting attacks (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Cross-site_scripting\">Cross-site_scripting<\/a>).<\/p><cite><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy<\/a><\/cite><\/blockquote>\n\n\n\n<p>You can read more about CSP and the various things it can do over on the <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy\" data-type=\"URL\" data-id=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy\" target=\"_blank\">MDN Web Docs<\/a>. There is a lot more that can and can&#8217;t be done with CSP more than just controlling what content can be loaded from where, but for Twitch Extensions we only need to consider the parts of the Policy that affect Twitch Extensions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Twitch Extension CSP Policy<\/h2>\n\n\n\n<p>Twitch is requiring Extension developer to declare the Connect, Img, and Media domains, which in the policy are <code>connect-src<\/code>, <code>img-src<\/code> and <code>media-src<\/code>. You can declare this in the <a rel=\"noreferrer noopener\" href=\"https:\/\/dev.twitch.tv\/console\/extensions\" data-type=\"URL\" data-id=\"https:\/\/dev.twitch.tv\/console\/extensions\" target=\"_blank\">Developer Console<\/a> for a version of your extension, under the  Capabilities tab.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"840\" height=\"509\" src=\"https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=840%2C509&#038;ssl=1\" alt=\"The New Extension Dashboard fields\" class=\"wp-image-52070\" srcset=\"https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=1024%2C621&amp;ssl=1 1024w, https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=300%2C182&amp;ssl=1 300w, https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=768%2C466&amp;ssl=1 768w, https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=1536%2C931&amp;ssl=1 1536w, https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?resize=1200%2C727&amp;ssl=1 1200w, https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/10\/extension_dashboard_newcspfields.png?w=1625&amp;ssl=1 1625w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/a><figcaption>The new Extension Dashboard fields<\/figcaption><\/figure>\n\n\n\n<p>Now, the items you enter here only apply when you are using Hosted Test (or release), since Hosted Test will use Twitch&#8217;s CDN, and thus Twitch&#8217;s Server which can load and use the relevant fields, but in localtesting (aka not the CDN) we need to set this up ourselves.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Local Testing a CSP<\/h2>\n\n\n\n<p>If you have been following this series, then you already have a Node\/Express server that will run a static output for you. We can easily add CSP headers to this server using a module called <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/helmetjs\/helmet\" data-type=\"URL\" data-id=\"https:\/\/github.com\/helmetjs\/helmet\" target=\"_blank\">Helmet<\/a>, generally speaking it&#8217;s wise to consider adding helmet (or CSP Headers in general) to any website you run to protect your users, but I digress!<\/p>\n\n\n\n<p>So, how to set this up for Testing with.<\/p>\n\n\n\n<p>Normally I&#8217;d say, on server start call the API to get the current extension settings from the console, however, the API at this time has not been updated to include the new fields, I raised a <a rel=\"noreferrer noopener\" href=\"https:\/\/twitch.uservoice.com\/forums\/310213-developers\/suggestions\/44236587-add-csp-fields-to-get-extensions\" data-type=\"URL\" data-id=\"https:\/\/twitch.uservoice.com\/forums\/310213-developers\/suggestions\/44236587-add-csp-fields-to-get-extensions\" target=\"_blank\">UserVoice requesting the new fields<\/a> be added to the endpoints. And you can upvote that <a rel=\"noreferrer noopener\" href=\"https:\/\/twitch.uservoice.com\/forums\/310213-developers\/suggestions\/44236587-add-csp-fields-to-get-extensions\" data-type=\"URL\" data-id=\"https:\/\/twitch.uservoice.com\/forums\/310213-developers\/suggestions\/44236587-add-csp-fields-to-get-extensions\" target=\"_blank\">here<\/a>.<\/p>\n\n\n\n<p>So for now, we&#8217;ll need to populate the CSP for Helmet manually.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring Helmet for CSP<\/h2>\n\n\n\n<p>The first thing I did was look at a released extension to see what the current CSP is, which I then split out into a object for configuring Helmet with. Then I looked at what the rig needs, and then looked at what you need to add to correctly simulate a CSP.<\/p>\n\n\n\n<p>The base CSP for a Twitch Extension is, here <code>twitch.client_id<\/code> is loaded from an external config file, and represents the location that Hosted Test and Release use to host your files. Which I&#8217;ll touch on later.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/*\nCurrent base CSP rules subject to change\n\nSee:\nhttps:\/\/discuss.dev.twitch.tv\/t\/new-extensions-policy-for-content-security-policy-csp-directives-and-timeline-for-enforcement\/33695\/2\n\nThis example is based off a live extension\n*\/\n\nlet contentSecurityPolicy = {\n    directives: {\n        defaultSrc: &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            `https:\/\/${twitch.client_id}.ext-twitch.tv`\n        ],\n        connectSrc: &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            `https:\/\/${twitch.client_id}.ext-twitch.tv`,\n            &#039;https:\/\/extension-files.twitch.tv&#039;,\n            &#039;https:\/\/www.google-analytics.com&#039;,\n            &#039;https:\/\/stats.g.doubleclick.net&#039;\n        ],\n        fontSrc:    &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            `https:\/\/${twitch.client_id}.ext-twitch.tv`,\n            &#039;https:\/\/fonts.googleapis.com&#039;,\n            &#039;https:\/\/fonts.gstatic.com&#039;\n        ],\n        imgSrc:     &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            &#039;data:&#039;,\n            &#039;blob:&#039;\n        ],\n        mediaSrc:   &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            &#039;data:&#039;,\n            &#039;blob:&#039;\n        ],\n        scriptSrc:  &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            `https:\/\/${twitch.client_id}.ext-twitch.tv`,\n            &#039;https:\/\/extension-files.twitch.tv&#039;,\n            &#039;https:\/\/www.google-analytics.com&#039;,\n            &#039;https:\/\/stats.g.doubleclick.net&#039;\n        ],\n        styleSrc:   &#x5B;\n            &quot;&#039;self&#039;&quot;,\n            &quot;&#039;unsafe-inline&#039;&quot;,\n            `https:\/\/${twitch.client_id}.ext-twitch.tv`,\n            &#039;https:\/\/fonts.googleapis.com&#039;\n        ],\n\n        frameAncestors: &#x5B;\n            &#039;https:\/\/supervisor.ext-twitch.tv&#039;,\n            &#039;https:\/\/extension-files.twitch.tv&#039;,\n            &#039;https:\/\/*.twitch.tv&#039;,\n            &#039;https:\/\/*.twitch.tech&#039;,\n            &#039;https:\/\/localhost.twitch.tv:*&#039;,\n            &#039;https:\/\/localhost.twitch.tech:*&#039;,\n            &#039;http:\/\/localhost.rig.twitch.tv:*&#039;\n        ]\n    }\n}\n\nconst helmet = require(&#039;helmet&#039;);\n\/*\nYou can use Security Headers to test your server, if this server is web accessible\nhttps:\/\/securityheaders.com\/\nIt&#039;ll test that your CSP is valid.\nBest testing done with an extension, on Twitch or in the rig!\n*\/\n\nconsole.log(&#039;Going to use the following CSP&#039;, contentSecurityPolicy);\n\napp.use(helmet({\n    contentSecurityPolicy\n}));\n<\/pre><\/div>\n\n\n<p>This I add after <code>app.listen<\/code> and before anything else! It does need to go before your <code>app.use<\/code> for <code>express.static<\/code><\/p>\n\n\n\n<p>This will configure your test server to use the base\/default CSP. And will log it out the full CSP to the console when you start the server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Extension Developer Rig<\/h2>\n\n\n\n<p>So the next step is how to enable your test server to work in the Twitch <a rel=\"noreferrer noopener\" href=\"https:\/\/dev.twitch.tv\/docs\/extensions\/rig\" data-type=\"URL\" data-id=\"https:\/\/dev.twitch.tv\/docs\/extensions\/rig\" target=\"_blank\">Extension Developer Rig<\/a>. I don&#8217;t often use the rig, but it&#8217;s handy for spot testing views and mobile when I don&#8217;t have my phone handy (or the Extension has not been iOS allow listed yet!)<\/p>\n\n\n\n<p>The Extension Rig is built in Electron, which means it will include calls to file and in testing it spot calls some other things.<\/p>\n\n\n\n<p>For the rig I add the following rules, which I append to the default CSP using a Config Switch.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/*\nshould we enable the Rig?\n\nThe rig being an electron app, will call some other things\nAs well as having a file:\/\/ based parent\n*\/\nif (csp_options.enable_rig) {\n    let rig_sources = {\n        connectSrc: &#x5B;\n            &#039;wss:\/\/pubsub-edge.twitch.tv&#039;\n        ],\n        frameAncestors: &#x5B;\n            &#039;http:\/\/localhost:*&#039;,\n            &#039;file:\/\/*&#039;,\n            &#039;filesystem:&#039;\n        ]\n    }\n\n    \/\/ append these to the CSP\n    for (let sourceType in rig_sources) {\n        for (let x=0;x&lt;rig_sources&#x5B;sourceType].length;x++) {\n            contentSecurityPolicy.directives&#x5B;sourceType].push(rig_sources&#x5B;sourceType]&#x5B;x]);\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<p>Nothing to silly there, but important if you are testing in the rig. Only enable this in your server when rig testing not testing on the Twitch website, as it&#8217;s overly permissive and might catch you out later.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">My Sources<\/h2>\n\n\n\n<p>The final thing to do is to setup your sources, now this gets a little weird, as a valid CSP rule can omit the schema of the URL (see note).<\/p>\n\n\n\n<p>For this example\/setup we are adding the content domains to all three CSP directives. Using this example you can adjust and modify this as granularly as you want.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/*\nDid we configure places that we can\/may load media from\nAnd yes we are just gonna glob them to all three groups\nFor example purposes\n*\/\ncsp_options.content_domains.forEach(domain =&gt; {\n    contentSecurityPolicy.directives.imgSrc.push(domain);\n    contentSecurityPolicy.directives.mediaSrc.push(domain);\n    contentSecurityPolicy.directives.connectSrc.push(domain);\n});\n<\/pre><\/div>\n\n\n<p><strong>Note<\/strong>: In testing browsers will not enable\/allow WSS if you declare a schema-less domain of <code>www.example.com<\/code>. So if you want WSS you need to declare it explicitly, for this I declare <code>wss:\/\/www.example.com<\/code> and <code>https:\/\/www.example.com<\/code> in the rule (not the lack of a trailing <code>\/<\/code>).<\/p>\n\n\n\n<p>I configure these schema+domains in an external configuration file for the server. Here is an example <code>config.json<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n{\n    &quot;listen&quot;: 8050,\n\n    &quot;csp_options&quot;: {\n        &quot;enable_rig&quot;: true,\n        &quot;report_uri&quot;: false,\n        &quot;ebs_domain&quot;: &quot;myebs.com&quot;,\n        &quot;content_domains&quot;: &#x5B;\n            &quot;https:\/\/mywebsite.com&quot;,\n            &quot;wss:\/\/mywebsite.com&quot;\n        ]\n    },\n\n    &quot;twitch&quot;: {\n        &quot;client_id&quot;: &quot;abcdefg&quot;\n    }\n}\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">EBS?<\/h2>\n\n\n\n<p>If your extension utilizes an EBS you&#8217;ll need to declare that and add it to your <code>connect-src<\/code>, however if you also load images from your EBS you can skip this step.<\/p>\n\n\n\n<p>I generally put my images and assets on a seperate server to my EBS, but for test purposes, this server example adds the EBS domain to all three declarations, for both schemas:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/*\nDid we configure an EBS to call\n*\/\nif (csp_options.ebs_domain) {\n    console.log(&#039;Appending EBS Domain&#039;);\n    let ebs_rules = {\n        imgSrc: &#x5B;\n            &#039;https:\/\/&#039; + csp_options.ebs_domain,\n            &#039;wss:\/\/&#039; + csp_options.ebs_domain\n        ],\n        mediaSrc: &#x5B;\n            &#039;https:\/\/&#039; + csp_options.ebs_domain,\n            &#039;wss:\/\/&#039; + csp_options.ebs_domain\n        ],\n        connectSrc: &#x5B;\n            &#039;https:\/\/&#039; + csp_options.ebs_domain,\n            &#039;wss:\/\/&#039; + csp_options.ebs_domain\n        ]\n    }\n\n    for (let sourceType in ebs_rules) {\n        for (let x=0;x&lt;ebs_rules&#x5B;sourceType].length;x++) {\n            contentSecurityPolicy.directives&#x5B;sourceType].push(ebs_rules&#x5B;sourceType]&#x5B;x]);\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Full Example!<\/h2>\n\n\n\n<p>I put all of this together as a full example over on my GitHub. <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/BarryCarlyon\/twitch_extension_blog_series\/tree\/main\/part_6\/extension\" data-type=\"URL\" data-id=\"https:\/\/github.com\/BarryCarlyon\/twitch_extension_blog_series\/tree\/main\/part_6\/extension\" target=\"_blank\">See Part 6 of the repository<\/a>. This provides a &#8220;rig&#8221; as described in <a href=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\" data-type=\"URL\" data-id=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\">Part 5<\/a> but with the additional CSP Fields included.<\/p>\n\n\n\n<p>To set this up do as follows<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Download the Example from GitHub<\/li><li>Copy <code>config_sample.json<\/code> to <code>config.json<\/code><\/li><li>Populate the twitch-&gt;client_id with your Extension Client ID<\/li><li>Revise the listen port if needed<\/li><li>Configure Your CSP options as needed, add the content domains as needed. And you EBS domain as needed.<\/li><li>If you load content from your EBS domain, set the <code>ebs_domain<\/code> to <code>false<\/code>, to avoid a duplicate declaration of a domain, or do not include your <code>ebs_domain<\/code> in your <code>content_domains<\/code><\/li><\/ul>\n\n\n\n<p>Once you have setup the server, you can the test your Rig via <em><a rel=\"noreferrer noopener\" href=\"https:\/\/securityheaders.com\/\" data-type=\"URL\" data-id=\"https:\/\/securityheaders.com\/\" target=\"_blank\">Security Headers<\/a><\/em> which will test that your CSP is valid, <strong>however<\/strong> this only works if your Test Server is accessible from the internet! Which if you follow <a href=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\" data-type=\"URL\" data-id=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/03\/15\/twitch-extensions-part-5\/\">Part 5&#8217;s note<\/a> will be for SSL testing purposes! And will only test that your CSP looks correct, not that it functions as intended!<\/p>\n\n\n\n<p>Then you can move on to testing your Extension and check that your CSP works as intended, then you do not have to move to hosted test and back to test changes to your CSP!<\/p>\n\n\n\n<p>If\/when the API is updated to return the new fields, I&#8217;ll add a part 6.5 (probably) which will use the API to get the details instead. Sods law you&#8217;ll add a domain to your Test Rig\/Server, and then forget to add the same domain to your Capabilities tab!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">DEADLINE<\/h2>\n\n\n\n<p>Twitch will begin to Enforce the new CSP policy on January 25th.<\/p>\n\n\n\n<p>Twitch closes for the holidays between Friday, 12\/17\/21 &#8211; Monday, 1\/3\/22. Twitch requests that Developers submit their extensions for review no later than Wednesday, 15th of December at 3PM PST.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Upcoming Winter Break<br><br>Attention, developers! Please note that the review team will be observing a winter holiday break from Friday, 12\/17\/21 &#8211; Monday, 1\/3\/22 and will not be performing Organization, Game, Chatbot Verification, or Extension reviews during this period of time. If you need a review completed prior to the holiday break, please submit your review request by no later than Wednesday, 12\/15 at 3PM PST. Thank you for your understanding &amp; happy holidays!<\/p><cite>From the <a href=\"https:\/\/dev.twitch.tv\/console\/\" data-type=\"URL\" data-id=\"https:\/\/dev.twitch.tv\/console\/\" target=\"_blank\" rel=\"noreferrer noopener\">Twitch Developers Console<\/a><\/cite><\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">ONE MORE THING: Report URI<\/h2>\n\n\n\n<p>Well what about the <code>report_uri<\/code>, that you saw in the <code>config.json<\/code> example?<\/p>\n\n\n\n<p>Well CSP provides a method to report CSP errors to a defined HTTPS POST endpoint. So whenever a CSP error occurs it can be reported to that HTTPS URL, very handy to help debug issues.<\/p>\n\n\n\n<p>So if you configure your <code>report_uri<\/code> to be the same URL as your Extension Test rig, but with <code>\/csp\/<\/code> on the end, so if your rig is at <code>https:\/\/mytestrig.com\/<\/code> then your CSP Report URI is <code>https:\/\/mytestrig.com\/csp\/<\/code><\/p>\n\n\n\n<p>You can capture and log these reports, for Express you will need to use the following code snippet, please note that a JSON payload is posted but using an alternative content-type, so you need to tell <code>express.json<\/code> to trigger on that <code>content-type<\/code> of <code>application\/csp-report<\/code><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/*\nThis will capture any CSP Report and dump log it to console\n*\/\napp.post(&#039;\/csp\/&#039;, express.json({\n    type: &#039;application\/csp-report&#039;\n}), (req,res) =&gt; {\n    console.log(req.body);\n\n    res.send(&#039;Ok&#039;);\n});\n<\/pre><\/div>\n\n\n<p>The <code>report-uri<\/code> documentation over on <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy\/report-uri\" data-type=\"URL\" data-id=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Security-Policy\/report-uri\" target=\"_blank\">MDN includes a PHP example<\/a>, if that is your cup of tea!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">An Update! Easier Development!<\/h2>\n\n\n\n<p>Added a small update to this post for easier testing with, first I took the entire CSP component and seperated it into a NPM module for easier usage and configuration.<\/p>\n\n\n\n<p>The module, `twitchextensioncsp` can be found over on <a rel=\"noreferrer noopener\" href=\"https:\/\/www.npmjs.com\/package\/twitchextensioncsp\" data-type=\"URL\" data-id=\"https:\/\/www.npmjs.com\/package\/twitchextensioncsp\" target=\"_blank\">NPM<\/a> and on <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/BarryCarlyon\/twitchextensioncsp\" data-type=\"URL\" data-id=\"https:\/\/github.com\/BarryCarlyon\/twitchextensioncsp\" target=\"_blank\">GitHub<\/a> and essentially just wraps Helmet for you and passes in the CSP Configuration with much less copy\/paste between extensions if you are working on multiples.<\/p>\n\n\n\n<p>And the Example simple server (remove the &#8220;build&#8221; system) is at <a href=\"https:\/\/github.com\/BarryCarlyon\/twitch_extension_blog_series\/tree\/main\/part_6_5\/extension\" data-type=\"URL\" data-id=\"https:\/\/github.com\/BarryCarlyon\/twitch_extension_blog_series\/tree\/main\/part_6_5\/extension\" target=\"_blank\" rel=\"noreferrer noopener\">Part 6.5 on the GitHub Repository<\/a><\/p>\n\n\n\n<p>For &#8220;ease&#8221; of use heres an &#8220;quick&#8221; static Express Server implementing the module, it will do the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Create an Express Server on port 8050<\/li><li>Invoke <code>twitchextensioncsp<\/code><\/li><li>Enable the Extension CSP to support the Twitch Extensions Rig<\/li><li>Add Img and Media and connect example domains<\/li><li>Static mount the <code>build<\/code> directory onto <code>extension<\/code> so your testing base URI is <code>http:\/\/localhost:8050\/extension\/<\/code> swap as needed depending on your SSL solution<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nconst express = require(&#039;express&#039;);\nconst app = express();\n\napp.listen(8050, function () {\n    console.log(&#039;booted express on 8050&#039;);\n});\n\nconst twitchextensioncsp = require(&#039;twitchextensioncsp&#039;);\napp.use(twitchextensioncsp({\n    clientID: &#039;abcdefg123456&#039;\n    enableRig: true,\n    imgSrc: &#x5B;\n        &#039;https:\/\/images.example.com&#039;\n    ],\n    mediaSrc: &#x5B;\n        &#039;https:\/\/videos.example.com&#039;\n    ],\n    connectSrc: &#x5B;\n        &#039;https:\/\/api.example.com&#039;\n    ]\n}));\n\napp.use(&#039;\/extension\/&#039;, express.static(__dirname + &#039;\/build\/&#039;));\n<\/pre><\/div>\n\n\n<p>If you refer to the README for <code>twitchextensioncsp<\/code> there are a handful of quick start examples for the CSP setup. As you do need to explicitly declare the Twitch CDN and Twitch API if you wish to use those in your Extension frontend!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part 5 we wrote about a suitable testing platform for building your extensions on, essentially we create a static content server, that mimics the Twitch CDN for testing with. Twitch Announced on the Forums that they are revising the CSP (Content Security Policy) that extensions use to protect and control what can be loaded. &hellip; <a href=\"https:\/\/barrycarlyon.co.uk\/wordpress\/2021\/11\/21\/twitch-extensions-part-6-dev-environment-updates-content-security-policy\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Twitch Extensions Part 6 \u2013 Dev Environment Updates &#8211; Content Security Policy!&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":52084,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[88,198,351,358,45,312],"tags":[82,381,380,279,313,354],"class_list":["post-52079","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code-geekery","category-code-snippets","category-extensions","category-extensions-series","category-geekery","category-twitch","tag-code","tag-content-security-policy","tag-csp","tag-security","tag-twitch","tag-twitch-extensions"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/barrycarlyon.co.uk\/wordpress\/wp-content\/uploads\/2021\/11\/CSP.png?fit=1231%2C587&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/posts\/52079","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/comments?post=52079"}],"version-history":[{"count":8,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/posts\/52079\/revisions"}],"predecessor-version":[{"id":52092,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/posts\/52079\/revisions\/52092"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/media\/52084"}],"wp:attachment":[{"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/media?parent=52079"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/categories?post=52079"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/barrycarlyon.co.uk\/wordpress\/wp-json\/wp\/v2\/tags?post=52079"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}