In the process of recently getting ready to improve another portion of blog-pwa, I ran into an interesting error when viewing my content security policy report-only setting in regards to trusted type implementation.
As a brief primer, trusted types is a recent specification that’s been in Chromium browsers since M83 and gives you the means to create policies that build non-spoofable typed values. The goal is to then allow said trusted type to be passed into a sink as a replacement for the string, preventing DOM-based cross-site scripting (DOM XSS) attacks. You can read more about it on the trusted types repo.
So, where’s the bug exactly? To be clear, it’s not really a bug in workbox, but rather the way I’ve defined the string that starts up the service worker. If we take a peak at the code, we can see I’m just straight up passing a string.
async __loadSw() {
if ('serviceWorker' in navigator) {
const wb = new Workbox('/service-worker.js');
// ...
}
}
That code works fine without trusted types, but the CSP error is warning me to say “hey, that string gets used as a createScriptURL, you gotta deal with that”. So, I wrote a little policy that checks to make sure it’s coming from where it should:
async __loadSw() {
if ('serviceWorker' in navigator) {
let swUrl;
// a little function to that our service worker is coming from where it should
// this is very similar to the 2.3 policy example, but you'd want to change that
// host hint hint since that probably won't work for you
const srcSw = url => {
const parsed = new URL(url, document.baseURI);
if (parsed.host === 'justinribeiro.com' || parsed.host === 'www.justinribeiro.com') {
return parsed.href;
}
throw new TypeError('invalid sw url');
};
// feature check if we have the type
if (window.trustedTypes && trustedTypes.createPolicy) {
// define the policy
const swPolicy = trustedTypes.createPolicy('swPolicy', {
createScriptURL: src => srcSw(src),
});
// create our trusted type
swUrl = swPolicy.createScriptURL('service-worker.js');
} else {
// we don't have trusted types or the polyfill didn't load
swUrl = srcSw('service-worker.js');
}
// use (hopefully) the trusted type
const wb = new Workbox(swUrl);
// ...
}
}
Not terribly complex and better than a no-op policy. The else
case ideally would go away with use of the polyfill though I haven’t vetted the perf behavior of that polyfill just yet and what the impact might be.
I’m still in the process of ironing out the details of my trusted types implementation, but the above is a night little way to get started without diving into the depths.