Skip to content

Commit

Permalink
Merge pull request from GHSA-5888-ffcr-r425
Browse files Browse the repository at this point in the history
* fix prototype polution bug

* update tests
  • Loading branch information
flybayer authored Feb 9, 2022
1 parent e0252a5 commit 0d68cd5
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/accessDeep.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isMap, isArray, isPlainObject, isSet } from './is';
import { includes } from './util';

const getNthKey = (value: Map<any, any> | Set<any>, n: number): any => {
const keys = value.keys();
Expand All @@ -10,7 +11,21 @@ const getNthKey = (value: Map<any, any> | Set<any>, n: number): any => {
return keys.next().value;
};

function validatePath(path: (string | number)[]) {
if (includes(path, '__proto__')) {
throw new Error('__proto__ is not allowed as a property');
}
if (includes(path, 'prototype')) {
throw new Error('prototype is not allowed as a property');
}
if (includes(path, 'constructor')) {
throw new Error('constructor is not allowed as a property');
}
}

export const getDeep = (object: object, path: (string | number)[]): object => {
validatePath(path);

path.forEach(key => {
object = (object as any)[key];
});
Expand All @@ -23,6 +38,8 @@ export const setDeep = (
path: (string | number)[],
mapper: (v: any) => any
): any => {
validatePath(path);

if (path.length === 0) {
return mapper(object);
}
Expand Down
60 changes: 60 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1000,3 +1000,63 @@ test('regression: `Object.create(null)` / object without prototype', () => {

expect(parsed.date).toBeInstanceOf(Date);
});

test('prototype pollution - __proto__', () => {
expect(() => {
SuperJSON.parse(
JSON.stringify({
json: {
myValue: 1337,
},
meta: {
referentialEqualities: {
myValue: ['__proto__.x'],
},
},
})
);
}).toThrowErrorMatchingInlineSnapshot(
`"__proto__ is not allowed as a property"`
);
expect((Object.prototype as any).x).toBeUndefined();
});

test('prototype pollution - prototype', () => {
expect(() => {
SuperJSON.parse(
JSON.stringify({
json: {
myValue: 1337,
},
meta: {
referentialEqualities: {
myValue: ['prototype.x'],
},
},
})
);
}).toThrowErrorMatchingInlineSnapshot(
`"prototype is not allowed as a property"`
);
});

test('prototype pollution - constructor', () => {
expect(() => {
SuperJSON.parse(
JSON.stringify({
json: {
myValue: 1337,
},
meta: {
referentialEqualities: {
myValue: ['constructor.prototype.x'],
},
},
})
);
}).toThrowErrorMatchingInlineSnapshot(
`"prototype is not allowed as a property"`
);

expect((Object.prototype as any).x).toBeUndefined();
});
1 change: 1 addition & 0 deletions src/is.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const isPlainObject = (
): payload is { [key: string]: any } => {
if (getType(payload) !== 'Object') return false;
if (Object.getPrototypeOf(payload) === null) return true;
if (payload === Object.prototype) return false;
return (
payload.constructor === Object &&
Object.getPrototypeOf(payload) === Object.prototype
Expand Down

0 comments on commit 0d68cd5

Please sign in to comment.