Skip to content

Instantly share code, notes, and snippets.

@ProfAvery
Last active November 7, 2024 05:19
Show Gist options
  • Save ProfAvery/5dbf5d1870f33d01745cc80c67b88f8a to your computer and use it in GitHub Desktop.
Save ProfAvery/5dbf5d1870f33d01745cc80c67b88f8a to your computer and use it in GitHub Desktop.
CPSC 455 - JavaScript prototypes, inheritance, and prototype pollution
// Prototype pollution, part 1
// See https://learning.oreilly.com/library/view/grokking-web-application/9781633438262/OEBPS/Text/11.html#heading_id_10
const food = {
munch() {
console.log('Eating')
},
}
const sandwich = {
__proto__: food,
bread: '',
filling: '',
}
const hotdog = {
__proto__: sandwich,
bread: 'bun',
filling: 'frankfurter',
}
const brainWorm = () => {
console.log("require('fs').rm('/', { recursive: true })")
}
console.log('food:', food)
console.log('sandwich:', sandwich)
console.log('hotdog:', hotdog)
hotdog.toString() // no output
food.toString = brainWorm
hotdog.toString() // oops
// Prototype pollution, part 1
// See https://learning.oreilly.com/library/view/grokking-web-application/9781633438262/OEBPS/Text/11.html#heading_id_10
function dangerousMerge(target, source) {
for (key in source) { // DON'T DO THIS
const value = source[key]
if (value instanceof Object) {
if (!target[key]) {
target[key] = {}
}
merge(target[key], value)
} else {
target[key] = value
}
}
}
// Straight from the textbook -- not actually dangerous
function safeMerge1(target, source) {
Object.entries(source).forEach(([key, value]) => {
if (value instanceof Object) {
if (!target[key]) {
target[key] = {}
}
merge(target[key], value)
} else {
target[key] = value
}
})
}
// Use for...of instead of for...in
function safeMerge2(target, source) {
for (const [key, value] of Object.entries(source)) {
if (value instanceof Object) {
if (!target[key]) {
target[key] = {}
}
merge(target[key], value)
} else {
target[key] = value
}
}
}
const innocentUser1 = {
name: 'Erin',
address: '1234 Happy Path',
phone: '555-1212'
}
innocentUser2 = { ...innocentUser1 }
vulnerableUser = { ...innocentUser1 }
const attack = {
name: 'sneaky_pete',
__proto__: {
access_code: 'brainworms'
}
}
safeMerge1(innocentUser1, attack)
safeMerge2(innocentUser2, attack)
dangerousMerge(vulnerableUser, attack)
console.log(innocentUser1)
console.log(innocentUser2)
console.log(vulnerableUser)
// See https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
personPrototype = {
greet() {
console.log(`hello, my name is ${this.name}!`)
},
}
// Pass prototype to Object.create()
alice = Object.create(personPrototype)
alice.name = 'Alice'
alice.greet()
// Assign prototype to __proto__ property
bob = { name: 'Bob' }
bob.__proto__ = personPrototype
bob.greet()
// Assign prototype property of constructor function
function PersonFunction(name) {
this.name = name
}
Object.assign(PersonFunction.prototype, personPrototype)
charlie = new PersonFunction('Charlie')
charlie.greet()
// Use class syntax
class PersonClass {
constructor(name) {
this.name = name
}
greet() {
console.log(`hello, my name is ${this.name}!`)
}
}
dan = new PersonClass('Dan')
dan.greet()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment