Skip to content

Commit

Permalink
fix proto pollution vulnerability (#2239)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus13371337 authored May 22, 2023
1 parent 2270ae5 commit 00bdd0a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 2 deletions.
37 changes: 37 additions & 0 deletions deno/lib/__tests__/record.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,40 @@ test("key and value getters", () => {
rec.valueSchema.parse(1234);
rec.element.parse(1234);
});

test("is not vulnerable to prototype pollution", async () => {
const rec = z.record(
z.object({
a: z.string(),
})
);

const data = JSON.parse(`
{
"__proto__": {
"a": "evil"
},
"b": {
"a": "good"
}
}
`);

const obj1 = rec.parse(data);
expect(obj1.a).toBeUndefined();

const obj2 = rec.safeParse(data);
expect(obj2.success).toBe(true);
if (obj2.success) {
expect(obj2.data.a).toBeUndefined();
}

const obj3 = await rec.parseAsync(data);
expect(obj3.a).toBeUndefined();

const obj4 = await rec.safeParseAsync(data);
expect(obj4.success).toBe(true);
if (obj4.success) {
expect(obj4.data.a).toBeUndefined();
}
});
5 changes: 4 additions & 1 deletion deno/lib/helpers/parseUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ export class ParseStatus {
if (key.status === "dirty") status.dirty();
if (value.status === "dirty") status.dirty();

if (typeof value.value !== "undefined" || pair.alwaysSet) {
if (
key.value !== "__proto__" &&
(typeof value.value !== "undefined" || pair.alwaysSet)
) {
finalObject[key.value] = value.value;
}
}
Expand Down
37 changes: 37 additions & 0 deletions src/__tests__/record.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,40 @@ test("key and value getters", () => {
rec.valueSchema.parse(1234);
rec.element.parse(1234);
});

test("is not vulnerable to prototype pollution", async () => {
const rec = z.record(
z.object({
a: z.string(),
})
);

const data = JSON.parse(`
{
"__proto__": {
"a": "evil"
},
"b": {
"a": "good"
}
}
`);

const obj1 = rec.parse(data);
expect(obj1.a).toBeUndefined();

const obj2 = rec.safeParse(data);
expect(obj2.success).toBe(true);
if (obj2.success) {
expect(obj2.data.a).toBeUndefined();
}

const obj3 = await rec.parseAsync(data);
expect(obj3.a).toBeUndefined();

const obj4 = await rec.safeParseAsync(data);
expect(obj4.success).toBe(true);
if (obj4.success) {
expect(obj4.data.a).toBeUndefined();
}
});
5 changes: 4 additions & 1 deletion src/helpers/parseUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ export class ParseStatus {
if (key.status === "dirty") status.dirty();
if (value.status === "dirty") status.dirty();

if (typeof value.value !== "undefined" || pair.alwaysSet) {
if (
key.value !== "__proto__" &&
(typeof value.value !== "undefined" || pair.alwaysSet)
) {
finalObject[key.value] = value.value;
}
}
Expand Down

0 comments on commit 00bdd0a

Please sign in to comment.