Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 04c06f8

Browse files
Disable prototype pollution on returned diff object
1 parent e37b759 commit 04c06f8

File tree

6 files changed

+66
-80
lines changed

6 files changed

+66
-80
lines changed

‎src/added.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isEmpty, isObject, hasOwnProperty } from './utils.js';
1+
import { isEmpty, isObject, hasOwnProperty,makeObjectWithoutPrototype } from './utils.js';
22

33
const addedDiff = (lhs, rhs) => {
44

@@ -19,7 +19,7 @@ const addedDiff = (lhs, rhs) => {
1919

2020
acc[key] = r[key];
2121
return acc;
22-
}, {});
22+
}, makeObjectWithoutPrototype());
2323
};
2424

2525
export default addedDiff;

‎src/deleted.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isEmpty, isObject, hasOwnProperty } from './utils.js';
1+
import { isEmpty, isObject, hasOwnProperty,makeObjectWithoutPrototype } from './utils.js';
22

33
const deletedDiff = (lhs, rhs) => {
44
if (lhs === rhs || !isObject(lhs) || !isObject(rhs)) return {};
@@ -18,7 +18,7 @@ const deletedDiff = (lhs, rhs) => {
1818

1919
acc[key] = undefined;
2020
return acc;
21-
}, {});
21+
}, makeObjectWithoutPrototype());
2222
};
2323

2424
export default deletedDiff;

‎src/diff.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isDate, isEmptyObject, isObject, hasOwnProperty } from './utils.js';
1+
import { isDate, isEmptyObject, isObject, hasOwnProperty,makeObjectWithoutPrototype } from './utils.js';
22

33
const diff = (lhs, rhs) => {
44
if (lhs === rhs) return {}; // equal return no diff
@@ -15,7 +15,7 @@ const diff = (lhs, rhs) => {
1515
}
1616

1717
return acc;
18-
}, {});
18+
}, makeObjectWithoutPrototype());
1919

2020
if (isDate(l) || isDate(r)) {
2121
if (l.valueOf() == r.valueOf()) return {};

‎src/updated.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isDate, isEmptyObject, isObject, hasOwnProperty } from './utils.js';
1+
import { isDate, isEmptyObject, isObject, hasOwnProperty,makeObjectWithoutPrototype } from './utils.js';
22

33
const updatedDiff = (lhs, rhs) => {
44
if (lhs === rhs) return {};
@@ -26,7 +26,7 @@ const updatedDiff = (lhs, rhs) => {
2626
}
2727

2828
return acc;
29-
}, {});
29+
}, makeObjectWithoutPrototype());
3030
};
3131

3232
export default updatedDiff;

‎src/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export const isEmpty = o => Object.keys(o).length === 0;
33
export const isObject = o => o != null && typeof o === 'object';
44
export const hasOwnProperty = (o, ...args) => Object.prototype.hasOwnProperty.call(o, ...args)
55
export const isEmptyObject = (o) => isObject(o) && isEmpty(o);
6+
export const makeObjectWithoutPrototype = () => Object.create(null);

‎test/pollution.test.js

Lines changed: 57 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,70 @@
11
import addedDiff from "../src/added";
2+
import updatedDiff from "../src/updated";
3+
import diff from "../src/diff";
4+
import deletedDiff from "../src/deleted";
25

36
describe("Prototype pollution", () => {
4-
test("Demonstrate prototype pollution globally across all objects", () => {
5-
const a = {};
6-
const b = new Object();
7-
8-
expect(a.hello).toBeUndefined();
9-
expect(b.hello).toBeUndefined();
10-
expect({}.hello).toBeUndefined();
11-
12-
b.__proto__.hello = "world";
13-
14-
expect(a.hello).toBe("world");
15-
expect(b.hello).toBe("world");
16-
expect({}.hello).toBe("world");
7+
describe("diff", () => {
8+
test("should not pollute returned diffs prototype", () => {
9+
const l = { role: "user" };
10+
const r = JSON.parse('{ "role": "user", "__proto__": { "role": "admin" } }');
11+
const difference = diff(l, r);
12+
13+
expect(l.role).toBe("user");
14+
expect(r.role).toBe("user");
15+
expect(difference.role).toBeUndefined();
16+
});
17+
18+
test("should not pollute returned diffs prototype on nested diffs", () => {
19+
const l = { about: { role: "user" } };
20+
const r = JSON.parse('{ "about": { "__proto__": { "role": "admin" } } }');
21+
const difference = addedDiff(l, r);
22+
23+
expect(l.about.role).toBe("user");
24+
expect(r.about.role).toBeUndefined();
25+
expect(difference.about.role).toBeUndefined();
26+
});
1727
});
1828

19-
test("addedDiff does not pollute global prototype when running diff with added `__proto__` key", () => {
20-
const a = { role: "user" };
21-
const b = JSON.parse('{ "__proto__": { "role": "admin" } }');
22-
23-
expect(a.role).toBe("user");
24-
expect(a.__proto__.role).toBeUndefined();
25-
expect(b.role).toBeUndefined();
26-
expect(b.__proto__.role).toBe("admin");
27-
expect({}.role).toBeUndefined();
28-
expect({}.__proto__role).toBeUndefined();
29-
30-
const difference = addedDiff(a, b);
31-
32-
expect(a.role).toBe("user");
33-
expect(a.__proto__.role).toBeUndefined();
34-
expect(b.__proto__.role).toBe("admin");
35-
expect(b.role).toBeUndefined();
36-
expect({}.role).toBeUndefined();
37-
expect({}.__proto__role).toBeUndefined();
38-
39-
expect(difference).toEqual({ __proto__: { role: "admin" } });
29+
describe("addedDiff", () => {
30+
test("addedDiff should not pollute returned diffs prototype", () => {
31+
const l = { role: "user" };
32+
const r = JSON.parse('{ "__proto__": { "role": "admin" } }');
33+
const difference = addedDiff(l, r);
34+
35+
expect(l.role).toBe("user");
36+
expect(r.role).toBeUndefined();
37+
expect(difference.role).toBeUndefined();
38+
});
39+
40+
test("should not pollute returned diffs prototype on nested diffs", () => {
41+
const l = { about: { role: "user" } };
42+
const r = JSON.parse('{ "about": { "__proto__": { "role": "admin" } } }');
43+
const difference = addedDiff(l, r);
44+
45+
expect(l.about.role).toBe("user");
46+
expect(r.about.role).toBeUndefined();
47+
expect(difference.about.role).toBeUndefined();
48+
});
4049
});
4150

42-
test("addedDiff does not pollute global prototype when running diff with added `__proto__` key generated from JSON.parse and mutating original left hand object", () => {
43-
let a = { role: "user" };
44-
// Note: Don't trust `JSON.parse`!!!
45-
const b = JSON.parse('{ "__proto__": { "role": "admin" } }');
46-
47-
expect(a.role).toBe("user");
48-
expect(a.__proto__.role).toBeUndefined();
49-
expect(b.role).toBeUndefined();
50-
expect(b.__proto__.role).toBe("admin");
51-
expect({}.role).toBeUndefined();
52-
expect({}.__proto__role).toBeUndefined();
53-
54-
// Note: although this does not pollute the global proto, it does pollute the original object. (Don't mutate kids!)
55-
a = addedDiff(a, b);
51+
test("updatedDiff should not pollute returned diffs prototype", () => {
52+
const l = { role: "user" };
53+
const r = JSON.parse('{ "role": "user", "__proto__": { "role": "admin" } }');
54+
const difference = updatedDiff(l, r);
5655

57-
expect(a.role).toBe("admin");
58-
expect(a.__proto__.role).toBe("admin");
59-
expect(b.__proto__.role).toBe("admin");
60-
expect(b.role).toBeUndefined();
61-
expect({}.role).toBeUndefined();
62-
expect({}.__proto__role).toBeUndefined();
56+
expect(l.role).toBe("user");
57+
expect(r.role).toBe("user");
58+
expect(difference.role).toBeUndefined();
6359
});
6460

65-
test("addedDiff does not pollute global prototype or original object when running diff with added `__proto__` key", () => {
66-
let a = { role: "user" };
67-
const b = { __proto__: { role: "admin" } };
68-
69-
expect(a.role).toBe("user");
70-
expect(a.__proto__.role).toBeUndefined();
71-
expect(b.role).toBe("admin");
72-
expect(b.__proto__.role).toBe("admin");
73-
expect({}.role).toBeUndefined();
74-
expect({}.__proto__role).toBeUndefined();
75-
76-
a = addedDiff(a, b);
61+
test("deletedDiff should not pollute returned diffs prototype", () => {
62+
const l = { role: "user" };
63+
const r = JSON.parse('{ "__proto__": { "role": "admin" } }');
64+
const difference = deletedDiff(l, r);
7765

78-
expect(a.role).toBeUndefined();
79-
expect(a.__proto__.role).toBeUndefined();
80-
expect(b.role).toBe("admin");
81-
expect(b.__proto__.role).toBe("admin");
82-
expect({}.role).toBeUndefined();
83-
expect({}.__proto__role).toBeUndefined();
66+
expect(l.role).toBe("user");
67+
expect(r.role).toBeUndefined();
68+
expect(difference.role).toBeUndefined();
8469
});
8570
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /