Skip to content

Instantly share code, notes, and snippets.

@warrenseine
Last active January 19, 2025 10:54
Show Gist options
  • Select an option

  • Save warrenseine/31129f9206f5fd82861944d5ff1a6189 to your computer and use it in GitHub Desktop.

Select an option

Save warrenseine/31129f9206f5fd82861944d5ff1a6189 to your computer and use it in GitHub Desktop.
Remove Proofpoint redirections from Outlook
// ==UserScript==
// @name Remove Proofpoint
// @namespace http://tampermonkey.net/
// @version 2025-01-19
// @description Replace Proofpoint links with the target directly
// @author Warren Seine
// @match https://outlook.office.com/mail/*
// @icon https://res.public.onecdn.static.microsoft/owamail/20241011003.19/resources/images/favicons/mail-seen.ico
// @downloadURL https://gist.github.com/warrenseine/31129f9206f5fd82861944d5ff1a6189/raw/remove-proofpoint.user.js
// @updateURL https://gist.github.com/warrenseine/31129f9206f5fd82861944d5ff1a6189/raw/remove-proofpoint.user.js
// @grant none
// ==/UserScript==
// ChatGPT-transformed and edited version of https://help.proofpoint.com/@api/deki/files/2775/urldecoder.py
class URLDefenseDecoder {
constructor() {
this.udPattern = /https:\/\/urldefense(?:\.proofpoint)?\.com\/(v[0-9])\//;
this.v1Pattern = /u=(?<url>.+?)&k=/;
this.v2Pattern = /u=(?<url>.+?)&[dc]=/;
this.v3Pattern = /v3\/__(?<url>.+?)__;(?<encBytes>.*?)!/;
this.v3TokenPattern = /\*(\*.)?/g;
this.v3SingleSlash = /^([a-z0-9+.-]+:\/)([^/].+)/i;
this.v3RunMapping = {};
const runValues = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
let runLength = 2;
for (const value of runValues) {
this.v3RunMapping[value] = runLength++;
}
}
decode(rewrittenUrl) {
const match = this.udPattern.exec(rewrittenUrl);
if (match) {
switch (match[1]) {
case "v1":
return this.decodeV1(rewrittenUrl);
case "v2":
return this.decodeV2(rewrittenUrl);
case "v3":
return this.decodeV3(rewrittenUrl);
default:
throw new Error(`Unrecognized version in: ${rewrittenUrl}`);
}
}
}
decodeV1(rewrittenUrl) {
const match = this.v1Pattern.exec(rewrittenUrl);
if (match) {
const urlEncodedUrl = match.groups.url;
const htmlEncodedUrl = decodeURIComponent(urlEncodedUrl);
return this.unescapeHtml(htmlEncodedUrl);
} else {
throw new Error("Error parsing URL");
}
}
decodeV2(rewrittenUrl) {
const match = this.v2Pattern.exec(rewrittenUrl);
if (match) {
const specialEncodedUrl = match.groups.url;
const urlEncodedUrl = specialEncodedUrl.replace(/-/g, '%').replace(/_/g, '/');
const htmlEncodedUrl = decodeURIComponent(urlEncodedUrl);
return this.unescapeHtml(htmlEncodedUrl);
} else {
throw new Error("Error parsing URL");
}
}
decodeV3(rewrittenUrl) {
const match = this.v3Pattern.exec(rewrittenUrl);
if (match) {
let url = match.groups.url;
const singleSlash = this.v3SingleSlash.exec(url);
if (singleSlash && singleSlash.length === 3) {
url = `${singleSlash[1]}/${singleSlash[2]}`;
}
const encodedUrl = decodeURIComponent(url);
if (!match.groups.encBytes) return encodedUrl;
const encBytes = match.groups.encBytes;
const decBytes = this.decodeBase64(encBytes.replace(/-/g, '+').replace(/_/g, '/'));
let currentMarker = 0;
const replaceToken = (token) => {
if (token === '*') {
return decBytes[currentMarker++];
}
if (token.startsWith('**')) {
const runLength = this.v3RunMapping[token[token.length - 1]];
const run = decBytes.substring(currentMarker, currentMarker + runLength);
currentMarker += runLength;
return run;
}
return token;
};
const substituteTokens = (text) => {
return text.replace(this.v3TokenPattern, (match) => replaceToken(match));
};
return substituteTokens(encodedUrl);
} else {
throw new Error("Error parsing URL");
}
}
decodeBase64(s) {
try {
return atob(s)
} catch (e) {
try {
return atob(s + '=')
} catch (e) {
return atob(s + '==')
}
}
}
unescapeHtml(str) {
const doc = new DOMParser().parseFromString(str, "text/html");
return doc.documentElement.textContent;
}
}
(function() {
'use strict';
const decoder = new URLDefenseDecoder();
setInterval(() => {
const links = document.querySelectorAll('a');
links.forEach(link => {
// Example usage in the browser
try {
const url = decoder.decode(link.href);
if (!url) return
let newLink = link.cloneNode(true);
newLink.href = url;
newLink.title = url;
link.replaceWith(newLink);
} catch (err) {
console.error(err);
}
});
}, 1000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment