From ade7587134a7f0933fb0300a83bbeb16427c5add Mon Sep 17 00:00:00 2001 From: Chen Shuaimin Date: Mon, 30 Mar 2026 16:12:59 +0800 Subject: [PATCH] fix(decode): handle unquoted Content-Disposition name parameter The name extraction unconditionally stripped the first and last characters assuming surrounding quotes. When the name value was unquoted (e.g. name=login_id), this truncated the field name (login_id -> ogin_i), making it inaccessible by its real key. --- spec/multipart_spec.lua | 32 ++++++++++++++++++++++++++++++++ src/multipart.lua | 3 +-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/spec/multipart_spec.lua b/spec/multipart_spec.lua index 5eb6561..9f11c2b 100644 --- a/spec/multipart_spec.lua +++ b/spec/multipart_spec.lua @@ -543,6 +543,38 @@ hello assert.are.same("... contents of file1.txt ...\nhello", all["files"]) end) + it("should decode a multipart body with unquoted field names", function() + local content_type = "multipart/form-data; boundary=AaB03x" + local body = [[ +--AaB03x +Content-Disposition: form-data; name=login_id + +larry123 +--AaB03x +Content-Disposition: form-data; name=api_key + +secret456 +--AaB03x--]] + + local res = Multipart(body, content_type) + assert.truthy(res) + + local param = res:get("login_id") + assert.truthy(param) + assert.are.same("login_id", param.name) + assert.are.same("larry123", param.value) + + param = res:get("api_key") + assert.truthy(param) + assert.are.same("api_key", param.name) + assert.are.same("secret456", param.value) + + local all = res:get_all() + assert.are.same(2, table_size(all)) + assert.are.same("larry123", all["login_id"]) + assert.are.same("secret456", all["api_key"]) + end) + it("should encode a multipart body", function() local content_type = "multipart/form-data; boundary=AaB03x" local body = [[ diff --git a/src/multipart.lua b/src/multipart.lua index 6789ae7..37a2e13 100644 --- a/src/multipart.lua +++ b/src/multipart.lua @@ -125,8 +125,7 @@ local function decode(body, boundary) if not is_header(v) then -- If it's not content disposition part local pos = v:match("^%s*[Nn][Aa][Mm][Ee]=()") if pos then - local current_value = v:match("^%s*([^=]*)", pos):gsub("%s*$", "") - part_name = sub(current_value, 2, #current_value - 1) + part_name = v:match('^%s*"?([^"]*)"?', pos):gsub("%s*$", "") end end end