Created
May 7, 2024 19:47
-
-
Save eligrey/1a2b8fdba1c403a21d24cdde4d829200 to your computer and use it in GitHub Desktop.
matchCSPEntry screenshot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Determine if a URL matches a CSP entry data flow, using native browser CSP | |
* capabilities. | |
* | |
* @param cspEntry - CSP entry data flow string | |
* @param url - A URL to check | |
* @returns True if the URL matches the data flow, otherwise false | |
*/ | |
export const matchCSPEntry = (cspEntry: string, url: URL): Promise<boolean> => | |
new Promise((resolve) => { | |
if (typeof document === 'undefined') { | |
throw new Error('CSP entry data flow matching requires the DOM API'); | |
} | |
const HTML = 'http://www.w3.org/1999/xhtml'; | |
const frame = (document.documentElement || document).appendChild( | |
document.createElementNS(HTML, 'iframe') as HTMLIFrameElement, | |
); | |
frame.style.display = 'none'; | |
// eslint-disable-next-line no-multi-assign | |
frame.width = frame.height = '0'; | |
const doc = frame.contentDocument!; | |
let resolved = false; | |
const resolveTest = (status: boolean): void => { | |
if (!resolved) { | |
resolved = true; | |
img.removeAttribute('src'); | |
img.remove(); | |
frame.remove(); | |
resolve(status); | |
} | |
}; | |
const isFirefox = | |
typeof navigator !== 'undefined' && | |
navigator.userAgent.includes('Firefox/'); | |
// Detect CSP violation | |
(isFirefox // Firefox doesn't send CSP violation events to iframe doc | |
? document | |
: doc | |
).addEventListener( | |
'securitypolicyviolation', | |
() => { | |
resolveTest(false); | |
}, | |
{ once: true }, | |
); | |
const meta = doc.createElementNS(HTML, 'meta') as HTMLMetaElement; | |
meta.httpEquiv = 'Content-Security-Policy'; | |
meta.content = `default-src ${cspEntry};`; | |
doc.head.appendChild(meta).remove(); | |
const img = doc.createElementNS(HTML, 'img') as HTMLImageElement; | |
doc.body.appendChild(img); | |
img.src = url.href; | |
// Resolve successfully if no CSP violations in ≥20ms | |
setTimeout(() => { | |
resolveTest(true); | |
}, 20); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment