diff options
-rw-r--r-- | content.js | 52 | ||||
-rw-r--r-- | icon.svg | 4 | ||||
-rw-r--r-- | manifest.json | 21 |
3 files changed, 77 insertions, 0 deletions
diff --git a/content.js b/content.js new file mode 100644 index 0000000..2329ae5 --- /dev/null +++ b/content.js @@ -0,0 +1,52 @@ +// shibboleth. decodes UTF-8 with an unholy combination of specific behaviour +// https://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html +function decodeUtf8(str) { + return decodeURIComponent(escape(str)) +} + +// decodes a string containing HTML entities +function decodeHtml(str) { + let doc = new DOMParser().parseFromString(str, 'text/html') + return doc.documentElement.textContent +} + +// decodes a "protected" "email" +function decode(data) { + const [key, ...encoded] = data.match(/.{2}/g).map(e => parseInt(e, 16)) + let bytes = encoded.map(e => String.fromCharCode(e ^ key)).join("") + + // not sure why the proprietary code decodes entities, but I'm not changing it + return decodeHtml(decodeUtf8(bytes)) +} + +// processes a document fragment for "protected" "emails" +function process(root) { + // mailto links + // format: <a href="/cdn-cgi/l/email-protection#{encrypted data}">...</a> + for (const node of root.querySelectorAll('a')) { + const url = new URL(node.href) + if (url.pathname === '/cdn-cgi/l/email-protection' && url.hash !== '') + node.href = `mailto:${decode(url.hash.slice(1))}` + } + + // everything else Cloudflare thinks is an email + // format: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="{encrypted data}">[email protected]</a> + for (const node of root.querySelectorAll('.__cf_email__')) + node.replaceWith(decode(node.getAttribute('data-cfemail'))) + + // <template> contents are not considered children of the element itself, so we + // need to recurse into them. I highly doubt this will ever come up in the wild, + // but since the proprietary code takes care of this edge-case, so should we. + for (const node of root.querySelectorAll('template')) + process(node.content) +} + +// check for the presence of the Cloudflare deobfuscation script. +// it doesn't make sense to run our stuff if this isn't in the page +// format: <script data-cfasync="false" src="/cdn-cgi/scripts/71a88165/cloudflare-static/email-decode.min.js"></script> +// (the hexadecimal part can change) +let scripts = document.querySelectorAll('script[src$="/cloudflare-static/email-decode.min.js"]') +if (scripts !== null) { + scripts.forEach(e => e.remove()) + process(document) +} diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..984ac72 --- /dev/null +++ b/icon.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 48 48" width="48" height="48"> + <rect x="0" y="0" width="48" height="48" fill="black" /> + <path d="m 20.8,9.2400001 h 3.28 q 3.24,0 3.56,0.04 2.24,0.36 3.8,1.9199999 0.6,0.6 0.96,1.2 0.72,1.16 0.96,2.6 l 0.08,0.28 v 9.08 L 33.4,33.8 q -0.28,2 -1.72,3.4 -1.28,1.24 -3.12,1.56 H 27.04 26.96 Q 25.4,38.52 24.28,37.6 24.04,37.44 24,37.4 l -0.24,0.16 q -1.16,0.92 -2.72,1.2 h -1.52 q -0.08,0 -0.12,0 -1.8,-0.32 -3.12,-1.56 -1.44,-1.4 -1.68,-3.4 -0.04,-0.36 -0.04,-4.16 0,-3.8 0.04,-4.16 0.24,-1.92 1.56,-3.28 1.32,-1.36 3.24,-1.64 0.28,-0.08 2.6,-0.08 h 2.36 l 0.2,0.08 q 0.96,0.32 1.28,1.32 l 0.04,0.2 v 5.68 l 0.04,5.72 0.08,0.2 q 0.12,0.36 0.32,0.6 0.24,0.32 0.64,0.48 0.36,0.2 0.8,0.2 0.44,0 0.8,-0.2 0.68,-0.32 0.96,-1.08 l 0.08,-0.2 V 24.64 15.6 Q 29.4,14 27.92,13.32 L 27.64,13.2 Q 27.2,13.04 24.56,13.04 h -0.44 l -3.16,0.04 q -1.44,0.16 -2.52,1.64 -0.08,0.16 -0.2,0.32 -0.28,0.48 -0.44,0.68 -0.4,0.4 -0.96,0.52 L 16.04,16.2 Q 15,15.96 14.64,14.96 14.56,14.72 14.56,14.44 l 0.08,-0.72 q 0.04,-0.12 0.12,-0.28 0.32,-0.64 0.88,-1.32 0.6,-0.76 1.32,-1.32 1.72,-1.3599999 3.84,-1.5599999 z M 22.04,28.88 22.08,24.32 h -1.04 q -1,0 -1.12,0.04 -0.12,0 -0.32,0.08 -0.88,0.28 -1.16,1.24 l -0.04,0.16 v 3.8 3.84 l 0.08,0.2 q 0.28,0.88 1.08,1.16 0.6,0.24 1.2,0.04 0.32,-0.12 0.6,-0.32 0.44,-0.32 0.64,-0.92 l 0.04,-0.16 z" fill="white" /> +</svg> diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..7411910 --- /dev/null +++ b/manifest.json @@ -0,0 +1,21 @@ +{ + "manifest_version": 2, + + "name": "[email protected]", + "version": "0.1", + + "description": "Deobfuscate e-mail addresses \"protected\" by Cloudflare", + + "icons": { + "48": "icon.svg", + "96": "icon.svg" + }, + + "content_scripts": [ + { + "matches": [ "*://*/*" ], + "js": [ "content.js" ], + "run_at": "document_end" + } + ] +} |