Skip to content

Commit 00bdd0a

Browse files
authoredMay 22, 2023
fix proto pollution vulnerability (#2239)
1 parent 2270ae5 commit 00bdd0a

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed
 

‎deno/lib/__tests__/record.test.ts

+37
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,40 @@ test("key and value getters", () => {
134134
rec.valueSchema.parse(1234);
135135
rec.element.parse(1234);
136136
});
137+
138+
test("is not vulnerable to prototype pollution", async () => {
139+
const rec = z.record(
140+
z.object({
141+
a: z.string(),
142+
})
143+
);
144+
145+
const data = JSON.parse(`
146+
{
147+
"__proto__": {
148+
"a": "evil"
149+
},
150+
"b": {
151+
"a": "good"
152+
}
153+
}
154+
`);
155+
156+
const obj1 = rec.parse(data);
157+
expect(obj1.a).toBeUndefined();
158+
159+
const obj2 = rec.safeParse(data);
160+
expect(obj2.success).toBe(true);
161+
if (obj2.success) {
162+
expect(obj2.data.a).toBeUndefined();
163+
}
164+
165+
const obj3 = await rec.parseAsync(data);
166+
expect(obj3.a).toBeUndefined();
167+
168+
const obj4 = await rec.safeParseAsync(data);
169+
expect(obj4.success).toBe(true);
170+
if (obj4.success) {
171+
expect(obj4.data.a).toBeUndefined();
172+
}
173+
});

‎deno/lib/helpers/parseUtil.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ export class ParseStatus {
136136
if (key.status === "dirty") status.dirty();
137137
if (value.status === "dirty") status.dirty();
138138

139-
if (typeof value.value !== "undefined" || pair.alwaysSet) {
139+
if (
140+
key.value !== "__proto__" &&
141+
(typeof value.value !== "undefined" || pair.alwaysSet)
142+
) {
140143
finalObject[key.value] = value.value;
141144
}
142145
}

‎src/__tests__/record.test.ts

+37
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,40 @@ test("key and value getters", () => {
133133
rec.valueSchema.parse(1234);
134134
rec.element.parse(1234);
135135
});
136+
137+
test("is not vulnerable to prototype pollution", async () => {
138+
const rec = z.record(
139+
z.object({
140+
a: z.string(),
141+
})
142+
);
143+
144+
const data = JSON.parse(`
145+
{
146+
"__proto__": {
147+
"a": "evil"
148+
},
149+
"b": {
150+
"a": "good"
151+
}
152+
}
153+
`);
154+
155+
const obj1 = rec.parse(data);
156+
expect(obj1.a).toBeUndefined();
157+
158+
const obj2 = rec.safeParse(data);
159+
expect(obj2.success).toBe(true);
160+
if (obj2.success) {
161+
expect(obj2.data.a).toBeUndefined();
162+
}
163+
164+
const obj3 = await rec.parseAsync(data);
165+
expect(obj3.a).toBeUndefined();
166+
167+
const obj4 = await rec.safeParseAsync(data);
168+
expect(obj4.success).toBe(true);
169+
if (obj4.success) {
170+
expect(obj4.data.a).toBeUndefined();
171+
}
172+
});

‎src/helpers/parseUtil.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ export class ParseStatus {
136136
if (key.status === "dirty") status.dirty();
137137
if (value.status === "dirty") status.dirty();
138138

139-
if (typeof value.value !== "undefined" || pair.alwaysSet) {
139+
if (
140+
key.value !== "__proto__" &&
141+
(typeof value.value !== "undefined" || pair.alwaysSet)
142+
) {
140143
finalObject[key.value] = value.value;
141144
}
142145
}

0 commit comments

Comments
 (0)
Please sign in to comment.