mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix: object pollution problem (#8499)
This commit is contained in:
@@ -136,12 +136,34 @@ export function exportAsJSONFile(obj: object, filename: string): void {
|
||||
}
|
||||
|
||||
export function setNestedProperty(obj: any, path: string, value: any) {
|
||||
const keys = path.split(/[\/.]/); // Split by / or .
|
||||
if (!obj || typeof obj !== "object") {
|
||||
throw new Error("Target must be a non-null object");
|
||||
}
|
||||
|
||||
if (!path || typeof path !== "string") {
|
||||
throw new Error("Path must be a non-empty string");
|
||||
}
|
||||
|
||||
const keys = path.split(/[\/.]/);
|
||||
|
||||
for (const key of keys) {
|
||||
if (
|
||||
!key ||
|
||||
key === "__proto__" ||
|
||||
key === "constructor" ||
|
||||
key === "prototype"
|
||||
) {
|
||||
throw new Error(`Invalid property name: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
let current = obj;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!current[key] || typeof current[key] !== "object") {
|
||||
if (!current.hasOwnProperty(key)) {
|
||||
current[key] = {};
|
||||
} else if (typeof current[key] !== "object" || current[key] === null) {
|
||||
current[key] = {};
|
||||
}
|
||||
current = current[key];
|
||||
|
||||
106
autogpt_platform/frontend/src/tests/util.spec.ts
Normal file
106
autogpt_platform/frontend/src/tests/util.spec.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { setNestedProperty } from "../lib/utils";
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: "simple property assignment",
|
||||
path: "name",
|
||||
value: "John",
|
||||
expected: { name: "John" },
|
||||
},
|
||||
{
|
||||
name: "nested property with dot notation",
|
||||
path: "user.settings.theme",
|
||||
value: "dark",
|
||||
expected: { user: { settings: { theme: "dark" } } },
|
||||
},
|
||||
{
|
||||
name: "nested property with slash notation",
|
||||
path: "user/settings/language",
|
||||
value: "en",
|
||||
expected: { user: { settings: { language: "en" } } },
|
||||
},
|
||||
{
|
||||
name: "mixed dot and slash notation",
|
||||
path: "user.settings/preferences.color",
|
||||
value: "blue",
|
||||
expected: { user: { settings: { preferences: { color: "blue" } } } },
|
||||
},
|
||||
{
|
||||
name: "overwrite primitive with object",
|
||||
path: "user.details",
|
||||
value: { age: 30 },
|
||||
expected: { user: { details: { age: 30 } } },
|
||||
},
|
||||
];
|
||||
|
||||
// Test Suite
|
||||
test.describe("Nested Property Setter Tests", () => {
|
||||
test.describe("Valid Usage Tests", () => {
|
||||
// Test secure implementation
|
||||
for (const { name, path, value, expected } of testCases) {
|
||||
test(name, () => {
|
||||
const obj = {};
|
||||
setNestedProperty(obj, path, value);
|
||||
expect(obj).toEqual(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test.describe("Security Improvements", () => {
|
||||
test("should throw error for null object", () => {
|
||||
expect(() => {
|
||||
setNestedProperty(null, "test", "value");
|
||||
}).toThrow("Target must be a non-null object");
|
||||
});
|
||||
|
||||
test("should throw error for undefined object", () => {
|
||||
expect(() => {
|
||||
setNestedProperty(undefined, "test", "value");
|
||||
}).toThrow("Target must be a non-null object");
|
||||
});
|
||||
|
||||
test("should throw error for non-object target", () => {
|
||||
expect(() => {
|
||||
setNestedProperty("string", "test", "value");
|
||||
}).toThrow("Target must be a non-null object");
|
||||
});
|
||||
|
||||
test("should throw error for empty path", () => {
|
||||
expect(() => {
|
||||
setNestedProperty({}, "", "value");
|
||||
}).toThrow("Path must be a non-empty string");
|
||||
});
|
||||
|
||||
test("should throw error for __proto__ access", () => {
|
||||
expect(() => {
|
||||
setNestedProperty({}, "__proto__.malicious", "attack");
|
||||
}).toThrow("Invalid property name: __proto__");
|
||||
});
|
||||
|
||||
test("should throw error for constructor access", () => {
|
||||
expect(() => {
|
||||
setNestedProperty({}, "constructor.prototype.malicious", "attack");
|
||||
}).toThrow("Invalid property name: constructor");
|
||||
});
|
||||
|
||||
test("should throw error for prototype access", () => {
|
||||
expect(() => {
|
||||
setNestedProperty({}, "obj.prototype.malicious", "attack");
|
||||
}).toThrow("Invalid property name: prototype");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Prototype Pollution Vulnerability Demo", () => {
|
||||
test("secure implementation prevents prototype pollution", () => {
|
||||
const obj = {};
|
||||
expect(() => {
|
||||
setNestedProperty(obj, "__proto__.polluted", true);
|
||||
}).toThrow("Invalid property name: __proto__");
|
||||
|
||||
// Verify no pollution occurred
|
||||
// @ts-ignore
|
||||
expect({}.polluted).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user