fix: Use putDirectMayBeIndex for potentially numeric properties and fix __proto__ handling

- Use putDirectMayBeIndex instead of putDirect for properties that could be numeric
- Fix __proto__ handling to create parent objects but skip the __proto__ property itself
- Update tests to match correct behavior

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-09-02 14:48:37 +00:00
parent ca8d2fad2e
commit a78aed8060
2 changed files with 16 additions and 7 deletions

View File

@@ -52,7 +52,8 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje
return;
// Simple key-value assignment - last value wins
result->putDirect(vm, Identifier::fromString(vm, key), jsString(vm, value));
// Use putDirectMayBeIndex since key could be numeric
result->putDirectMayBeIndex(globalObject, Identifier::fromString(vm, key), jsString(vm, value));
return;
}
@@ -130,9 +131,7 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje
return; // Malformed
String innerKey = remainder.substring(1, closeBracket - 1);
if (innerKey == "__proto__"_s)
return;
// Determine if this should be an array (numeric index) or object (string key)
unsigned index = 0;
bool isIndex = isArrayIndex(innerKey, index);
@@ -185,13 +184,18 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje
array->putDirectIndex(globalObject, index, nestedObj);
}
} else {
// Skip __proto__ for security
if (innerKey == "__proto__"_s)
return;
JSValue existingNested = container->getDirect(vm, Identifier::fromString(vm, innerKey));
if (!existingNested.isEmpty() && existingNested.isObject()) {
nestedObj = asObject(existingNested);
} else {
nestedObj = constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
container->putDirect(vm, Identifier::fromString(vm, innerKey), nestedObj);
// Use putDirectMayBeIndex since innerKey could be numeric
container->putDirectMayBeIndex(globalObject, Identifier::fromString(vm, innerKey), nestedObj);
}
}
@@ -223,7 +227,11 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje
JSArray* array = jsCast<JSArray*>(container);
array->putDirectIndex(globalObject, index, jsString(vm, value));
} else {
container->putDirect(vm, Identifier::fromString(vm, innerKey), jsString(vm, value));
// Skip __proto__ for security
if (innerKey != "__proto__"_s) {
// Use putDirectMayBeIndex since innerKey could be numeric
container->putDirectMayBeIndex(globalObject, Identifier::fromString(vm, innerKey), jsString(vm, value));
}
}
}
}

View File

@@ -104,9 +104,10 @@ test("parseQueryParams - complex nested structure", () => {
test("parseQueryParams - __proto__ is ignored for security", () => {
const result = parseQueryParams("__proto__=evil&user[__proto__]=bad&normal=ok");
// When __proto__ is the only key for an object, the object is not created
// __proto__ keys are ignored, but the parent object is still created
expect(result).toEqual({
normal: "ok",
user: {},
});
// Verify prototype wasn't polluted