Skip to content

Instantly share code, notes, and snippets.

@moeriki
Last active March 28, 2024 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moeriki/0cd8cf8eb046deaa89a34e6e4137b380 to your computer and use it in GitHub Desktop.
Save moeriki/0cd8cf8eb046deaa89a34e6e4137b380 to your computer and use it in GitHub Desktop.
<script type="module">
/* eslint-env browser */
const globalObjects = {
// JS Prototypes
Array,
Boolean,
Date,
Error,
Function,
Map,
Number,
Object,
Promise,
RegExp,
Set,
String,
Symbol,
// Web Globals
console,
// Web Prototypes
Node,
Element,
HTMLElement,
Document,
Window,
Event,
XMLHttpRequest,
HTMLCollection,
NodeList,
NamedNodeMap,
};
function forEachProperty(callback) {
const filteredObjectNames = new Set();
for (const [objectName, globalValue] of Object.entries(globalObjects)) {
const descriptors = Object.getOwnPropertyDescriptors(
globalValue.prototype == undefined ? globalValue : globalValue.prototype,
);
for (const [propertyName, descriptor] of Object.entries(descriptors)) {
if (callback(objectName, propertyName, descriptor)) {
filteredObjectNames.add(objectName);
}
}
}
return filteredObjectNames;
}
const originals = {};
forEachProperty((prototypeName, propertyName, descriptor) => {
originals[prototypeName] ??= {};
originals[prototypeName][propertyName] = descriptor;
});
function checkPrototypePolution() {
const cleanPrototypeNames = forEachProperty(
(objectName, propertyName, descriptor) => {
const ogDescriptor = originals[objectName][propertyName];
if (ogDescriptor == undefined) {
console.warn(`🚨 ${objectName}.prototype.${propertyName} is new`);
return false;
}
if (descriptor.configurable !== ogDescriptor.configurable) {
console.warn(
`🚨 ${objectName}#${propertyName} changed configurable to ${descriptor.enumerable}`,
);
}
if (descriptor.enumerable !== ogDescriptor.enumerable) {
console.warn(
`🚨 ${objectName}#${propertyName} changed enumerable to ${descriptor.enumerable}`,
);
}
if (descriptor.writable !== ogDescriptor.writable) {
console.warn(
`🚨 ${objectName}#${propertyName} changed writable to ${descriptor.enumerable}`,
);
}
if ('get' in descriptor && descriptor.get !== ogDescriptor.get) {
console.error(
`🚨 ${objectName}#${propertyName} getter is polluted`,
descriptor.get.toString(),
);
return false;
}
if ('set' in descriptor && descriptor.set !== ogDescriptor.set) {
console.error(
`🚨 ${objectName}#${propertyName} setter is polluted`,
descriptor.set.toString(),
);
return false;
}
if ('value' in descriptor && descriptor.value !== ogDescriptor.value) {
console.error(`🚨 ${objectName}#${propertyName} is polluted`);
return false;
}
return true;
},
);
for (const prototypeName of cleanPrototypeNames) {
console.info(`✅ ${prototypeName} is clean`);
}
}
setTimeout(checkPrototypePolution, 2000);
globalThis['__checkPrototypePolution'] = checkPrototypePolution;
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment