From ca8d2fad2e59843d4e0321173d4e983ebbfe3092 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:43:45 +0000 Subject: [PATCH] [autofix.ci] apply automated fixes --- src/bun.js/bindings/BunRequestParams.cpp | 100 ++++++++++---------- src/bun.js/bindings/BunRequestParams.h | 2 +- src/bun.js/bindings/JSBunRequest.cpp | 11 +-- test/js/bun/http/bun-request-query.test.ts | 53 ++++++----- test/js/bun/http/parse-query-params.test.ts | 6 +- 5 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/bun.js/bindings/BunRequestParams.cpp b/src/bun.js/bindings/BunRequestParams.cpp index f737fb4db8..ab070b2988 100644 --- a/src/bun.js/bindings/BunRequestParams.cpp +++ b/src/bun.js/bindings/BunRequestParams.cpp @@ -14,26 +14,26 @@ static bool isArrayIndex(const String& key, unsigned& index) { if (key.isEmpty()) return false; - + // Check if all characters are digits for (auto c : StringView(key).codeUnits()) { if (!isASCIIDigit(c)) return false; } - + // Parse the integer auto parseResult = parseInteger(StringView(key)); if (!parseResult.has_value()) return false; - + index = parseResult.value(); - + // Prevent creating huge sparse arrays - limit to reasonable size // Rails typically limits array indices to prevent DoS // We'll use a high limit that prevents obvious abuse if (index > 10000) return false; - + return true; } @@ -41,70 +41,70 @@ static bool isArrayIndex(const String& key, unsigned& index) static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObject* result, const String& key, const String& value) { auto& vm = globalObject->vm(); - + // Find the first bracket size_t bracketPos = key.find('['); - + // No brackets - simple key-value pair if (bracketPos == notFound) { // Ignore __proto__ for security if (key == "__proto__"_s) return; - + // Simple key-value assignment - last value wins result->putDirect(vm, Identifier::fromString(vm, key), jsString(vm, value)); return; } - + // Extract the base key String baseKey = key.substring(0, bracketPos); if (baseKey == "__proto__"_s) return; - + // Parse the rest of the key to determine structure String remainder = key.substring(bracketPos); - + // Get existing value at baseKey JSValue existing = result->getDirect(vm, Identifier::fromString(vm, baseKey)); - + // Handle [] notation (array append) if (remainder.startsWith("[]"_s)) { JSArray* array = nullptr; - + // Check if we already have a value at this key if (!existing.isEmpty()) { if (!existing.isObject()) return; // Can't convert primitive to array - + JSObject* obj = asObject(existing); if (!obj->inherits()) return; // Type conflict - it's an object, not an array - + array = jsCast(obj); } else { // Create new array array = JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0); result->putDirect(vm, Identifier::fromString(vm, baseKey), array); } - + // Check if there's more nesting after [] if (remainder.length() > 2 && remainder[2] == '[') { // Handle cases like users[][name] - create object and recursively parse String nestedRemainder = remainder.substring(2); - + // Create a new object for this array element JSObject* nestedObj = constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure()); array->putDirectIndex(globalObject, array->length(), nestedObj); - + // Recursively parse the nested structure // Remove the leading [ and find the closing ] size_t closeBracket = nestedRemainder.find(']'); if (closeBracket != notFound) { String nestedKey = nestedRemainder.substring(1, closeBracket - 1); - String afterBracket = closeBracket + 1 < nestedRemainder.length() - ? nestedRemainder.substring(closeBracket + 1) + String afterBracket = closeBracket + 1 < nestedRemainder.length() + ? nestedRemainder.substring(closeBracket + 1) : String(); - + if (afterBracket.isEmpty()) { // Simple nested property like users[][name] if (nestedKey != "__proto__"_s) { @@ -123,31 +123,31 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje } return; } - + // Handle [key] notation (could be array index or object property) size_t closeBracket = remainder.find(']'); if (closeBracket == notFound) 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); - + // Get or create the container (array or object) JSObject* container = nullptr; bool isArray = false; - + if (!existing.isEmpty()) { if (!existing.isObject()) return; // Can't index into primitive - + container = asObject(existing); isArray = container->inherits(); - + // Type consistency check if (isIndex && !isArray) return; // Trying to use array index on object @@ -164,20 +164,20 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje } result->putDirect(vm, Identifier::fromString(vm, baseKey), container); } - + // Check if there's more nesting size_t nextBracket = remainder.find('[', closeBracket + 1); if (nextBracket != notFound) { // More nesting - recursively parse String nestedRemainder = remainder.substring(closeBracket + 1); - + // Get or create nested object JSObject* nestedObj = nullptr; - + if (isArray) { JSArray* array = jsCast(container); JSValue existingAtIndex = index < array->length() ? array->getIndexQuickly(index) : JSValue(); - + if (!existingAtIndex.isEmpty() && existingAtIndex.isObject()) { nestedObj = asObject(existingAtIndex); } else { @@ -186,7 +186,7 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje } } else { JSValue existingNested = container->getDirect(vm, Identifier::fromString(vm, innerKey)); - + if (!existingNested.isEmpty() && existingNested.isObject()) { nestedObj = asObject(existingNested); } else { @@ -194,16 +194,16 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje container->putDirect(vm, Identifier::fromString(vm, innerKey), nestedObj); } } - + // Parse the nested structure if (nestedRemainder.startsWith("["_s) && nestedRemainder.length() > 1) { size_t endBracket = nestedRemainder.find(']'); if (endBracket != notFound) { String propertyName = nestedRemainder.substring(1, endBracket - 1); - String afterProperty = endBracket + 1 < nestedRemainder.length() - ? nestedRemainder.substring(endBracket + 1) + String afterProperty = endBracket + 1 < nestedRemainder.length() + ? nestedRemainder.substring(endBracket + 1) : String(); - + if (afterProperty.isEmpty()) { // Simple property assignment if (propertyName != "__proto__"_s) { @@ -231,22 +231,22 @@ static void parseRailsStyleParams(JSC::JSGlobalObject* globalObject, JSC::JSObje JSObject* parseQueryParams(JSGlobalObject* globalObject, const String& queryString) { auto& vm = globalObject->vm(); - + // Create result object with null prototype for security JSObject* queryObject = constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure()); - + if (queryString.isEmpty()) { return queryObject; } - + // Parse query string using WebKit's URLParser auto params = WTF::URLParser::parseURLEncodedForm(queryString); - + // Process each parameter with Rails-style parsing for (const auto& param : params) { parseRailsStyleParams(globalObject, queryObject, param.key, param.value); } - + return queryObject; } @@ -256,35 +256,35 @@ JSObject* parseURLQueryParams(JSGlobalObject* globalObject, const String& urlStr URL url(urlString); StringView queryView = url.query(); String queryString = queryView.toString(); - + return parseQueryParams(globalObject, queryString); } // Export for testing -JSC_DEFINE_HOST_FUNCTION(jsBunParseQueryParams, (JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(jsBunParseQueryParams, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { auto& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - + if (callFrame->argumentCount() < 1) { return JSValue::encode(jsUndefined()); } - + JSValue arg = callFrame->argument(0); if (!arg.isString()) { return JSValue::encode(jsUndefined()); } - + String queryString = arg.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - + JSObject* result = parseQueryParams(globalObject, queryString); return JSValue::encode(result); } -extern "C" JSC::EncodedJSValue Bun__parseQueryParams(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +extern "C" JSC::EncodedJSValue Bun__parseQueryParams(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { return jsBunParseQueryParams(globalObject, callFrame); } -} // namespace Bun \ No newline at end of file +} // namespace Bun diff --git a/src/bun.js/bindings/BunRequestParams.h b/src/bun.js/bindings/BunRequestParams.h index 3880fc95da..6d74555f85 100644 --- a/src/bun.js/bindings/BunRequestParams.h +++ b/src/bun.js/bindings/BunRequestParams.h @@ -18,4 +18,4 @@ JSC::JSObject* parseURLQueryParams(JSC::JSGlobalObject* globalObject, const WTF: JSC_DECLARE_HOST_FUNCTION(jsBunParseQueryParams); extern "C" JSC::EncodedJSValue Bun__parseQueryParams(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -} // namespace Bun \ No newline at end of file +} // namespace Bun diff --git a/src/bun.js/bindings/JSBunRequest.cpp b/src/bun.js/bindings/JSBunRequest.cpp index 5edeb04238..5c5d9800eb 100644 --- a/src/bun.js/bindings/JSBunRequest.cpp +++ b/src/bun.js/bindings/JSBunRequest.cpp @@ -250,26 +250,25 @@ JSC_DEFINE_CUSTOM_GETTER(jsJSBunRequestGetCookies, (JSC::JSGlobalObject * global return JSValue::encode(cookies); } - JSC_DEFINE_CUSTOM_GETTER(jsJSBunRequestGetQuery, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) { auto& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - + JSBunRequest* request = jsDynamicCast(JSValue::decode(thisValue)); if (!request) return JSValue::encode(jsUndefined()); - + // Get the URL from the request JSValue urlValue = request->get(globalObject, Identifier::fromString(vm, "url"_s)); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - + if (!urlValue.isString()) return JSValue::encode(jsUndefined()); - + String urlString = urlValue.toWTFString(globalObject); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - + // Use the extracted parsing function JSObject* queryObject = parseURLQueryParams(globalObject, urlString); return JSValue::encode(queryObject); diff --git a/test/js/bun/http/bun-request-query.test.ts b/test/js/bun/http/bun-request-query.test.ts index dc840e266a..afd960fa96 100644 --- a/test/js/bun/http/bun-request-query.test.ts +++ b/test/js/bun/http/bun-request-query.test.ts @@ -1,5 +1,4 @@ -import { test, expect } from "bun:test"; -import { bunEnv, bunExe } from "harness"; +import { expect, test } from "bun:test"; console.log("Test file loaded"); @@ -17,7 +16,7 @@ test("req.query - simple parameters", async () => { const res = await fetch(`${server.url}?name=john&age=30&active=true`); const data = await res.json(); - + expect(data).toEqual({ name: "john", age: "30", @@ -39,7 +38,7 @@ test("req.query - empty query string", async () => { const res = await fetch(`${server.url}`); const data = await res.json(); - + expect(data).toEqual({}); }); @@ -57,7 +56,7 @@ test("req.query - URL encoded values", async () => { const res = await fetch(`${server.url}?message=Hello%20World&special=%40%23%24%25`); const data = await res.json(); - + expect(data).toEqual({ message: "Hello World", special: "@#$%", @@ -78,7 +77,7 @@ test("req.query - Rails-style nested objects", async () => { const res = await fetch(`${server.url}?user[name]=john&user[age]=30&user[email]=john@example.com`); const data = await res.json(); - + expect(data).toEqual({ user: { name: "john", @@ -100,9 +99,11 @@ test("req.query - Rails-style deeply nested objects", async () => { }, }); - const res = await fetch(`${server.url}?person[address][street]=123%20Main&person[address][city]=Portland&person[name]=Bob`); + const res = await fetch( + `${server.url}?person[address][street]=123%20Main&person[address][city]=Portland&person[name]=Bob`, + ); const data = await res.json(); - + expect(data).toEqual({ person: { address: { @@ -128,7 +129,7 @@ test("req.query - Rails-style arrays with empty brackets", async () => { const res = await fetch(`${server.url}?ids[]=1&ids[]=2&ids[]=3`); const data = await res.json(); - + expect(data).toEqual({ ids: ["1", "2", "3"], }); @@ -148,7 +149,7 @@ test("req.query - Rails-style indexed arrays", async () => { const res = await fetch(`${server.url}?items[0]=apple&items[1]=banana&items[2]=orange`); const data = await res.json(); - + expect(data).toEqual({ items: ["apple", "banana", "orange"], }); @@ -170,7 +171,7 @@ test("req.query - Rails-style indexed arrays", async () => { // // const res = await fetch(`${server.url}?user[tags][]=admin&user[tags][]=developer&user[name]=alice`); // const data = await res.json(); -// +// // expect(data).toEqual({ // user: { // tags: ["admin", "developer"], @@ -193,7 +194,7 @@ test("req.query - duplicate keys (last wins)", async () => { const res = await fetch(`${server.url}?color=red&color=blue&color=green`); const data = await res.json(); - + // In simple key-value pairs, last value wins expect(data).toEqual({ color: "green", @@ -214,7 +215,7 @@ test("req.query - mixed simple and nested parameters", async () => { const res = await fetch(`${server.url}?simple=value&nested[key]=nestedValue&array[]=1&array[]=2`); const data = await res.json(); - + expect(data).toEqual({ simple: "value", nested: { @@ -238,7 +239,7 @@ test("req.query - numeric-looking keys", async () => { const res = await fetch(`${server.url}?123=numeric&0=zero&normal=text`); const data = await res.json(); - + expect(data).toEqual({ "123": "numeric", "0": "zero", @@ -260,7 +261,7 @@ test("req.query - empty values", async () => { const res = await fetch(`${server.url}?empty=&also_empty&has_value=yes`); const data = await res.json(); - + expect(data).toEqual({ empty: "", also_empty: "", @@ -282,7 +283,7 @@ test("req.query - complex nested structure", async () => { const res = await fetch(`${server.url}?users[0][name]=alice&users[0][age]=25&users[1][name]=bob&users[1][age]=30`); const data = await res.json(); - + expect(data).toEqual({ users: [ { name: "alice", age: "25" }, @@ -305,13 +306,13 @@ test("req.query - __proto__ is ignored for security", async () => { const res = await fetch(`${server.url}?__proto__=evil&user[__proto__]=bad&normal=ok`); const data = await res.json(); - + // __proto__ keys should be ignored expect(data).toEqual({ normal: "ok", user: {}, }); - + // Verify prototype wasn't polluted expect(Object.prototype.hasOwnProperty("evil")).toBe(false); }); @@ -336,7 +337,7 @@ test("req.query - null prototype object", async () => { const res = await fetch(`${server.url}?test=value`); const data = await res.json(); - + expect(data.hasNullProto).toBe(true); expect(data.query).toEqual({ test: "value" }); }); @@ -355,7 +356,7 @@ test("req.query - special characters in keys", async () => { const res = await fetch(`${server.url}?key%20with%20spaces=value&symbols!%40%23=test`); const data = await res.json(); - + expect(data).toEqual({ "key with spaces": "value", "symbols!@#": "test", @@ -377,7 +378,7 @@ test("req.query - works only with routes (Bun.serve)", async () => { const res = await fetch(`${server.url}?test=value`); const data = await res.json(); - + // Without routes, req.query won't be available (regular Request, not BunRequest) expect(data.hasQuery).toBe(false); }); @@ -388,7 +389,7 @@ test("req.query - with routes", async () => { routes: { "/test": { GET(req) { - return Response.json({ + return Response.json({ hasQuery: "query" in req, query: req.query, }); @@ -399,7 +400,7 @@ test("req.query - with routes", async () => { const res = await fetch(`${server.url}/test?foo=bar`); const data = await res.json(); - + expect(data.hasQuery).toBe(true); expect(data.query).toEqual({ foo: "bar" }); }); @@ -418,7 +419,7 @@ test("req.query - sparse indexed arrays", async () => { const res = await fetch(`${server.url}?arr[0]=first&arr[2]=third&arr[5]=sixth`); const data = await res.json(); - + // Sparse arrays will have null in JSON for missing indices expect(data).toEqual({ arr: ["first", null, "third", null, null, "sixth"], @@ -441,9 +442,9 @@ test("req.query - array and object type conflict", async () => { // the first type wins and conflicting params are ignored const res = await fetch(`${server.url}?items[]=array&items[key]=object`); const data = await res.json(); - + // First param established items as array, so object notation is ignored expect(data).toEqual({ items: ["array"], }); -}); \ No newline at end of file +}); diff --git a/test/js/bun/http/parse-query-params.test.ts b/test/js/bun/http/parse-query-params.test.ts index 0c47abec30..99d64acb48 100644 --- a/test/js/bun/http/parse-query-params.test.ts +++ b/test/js/bun/http/parse-query-params.test.ts @@ -1,5 +1,5 @@ -import { test, expect } from "bun:test"; import { parseQueryParams } from "bun:internal-for-testing"; +import { expect, test } from "bun:test"; test("parseQueryParams - simple parameters", () => { const result = parseQueryParams("name=john&age=30&active=true"); @@ -108,7 +108,7 @@ test("parseQueryParams - __proto__ is ignored for security", () => { expect(result).toEqual({ normal: "ok", }); - + // Verify prototype wasn't polluted expect(Object.prototype.hasOwnProperty("evil")).toBe(false); }); @@ -135,4 +135,4 @@ test("parseQueryParams - array and object type conflict", () => { expect(result).toEqual({ items: ["array"], }); -}); \ No newline at end of file +});