Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugins/request-size-limiting): Check the file size when the request body buffered to a temporary file #13303

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: "**Request Size Limiting**: Fixed an issue where the body size doesn't get checked when the request body is buffered to a temporary file."
type: bugfix
scope: Plugin
10 changes: 10 additions & 0 deletions kong/plugins/request-size-limiting/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
local strip = require("kong.tools.string").strip
local kong_meta = require "kong.meta"
local tonumber = tonumber
local lfs = require "lfs"


local RequestSizeLimitingHandler = {}
Expand Down Expand Up @@ -52,6 +53,15 @@ function RequestSizeLimitingHandler:access(conf)
local data = kong.request.get_raw_body()
if data then
check_size(#data, conf.allowed_payload_size, headers, conf.size_unit)
else
-- Check the file size when the request body buffered to a temporary file
local body_filepath = ngx.req.get_body_file()
if body_filepath then
local file_size = lfs.attributes(body_filepath, "size")
check_size(file_size, conf.allowed_payload_size, headers, conf.size_unit)
else
kong.log.warn("missing request body")
end
end
end
end
Expand Down
211 changes: 145 additions & 66 deletions spec/03-plugins/12-request-size-limiting/01-access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local unit_multiplication_factor = handler.unit_multiplication_factor

local TEST_SIZE = 2
local MB = 2^20
local KB = 2^10


for _, strategy in helpers.each_strategy() do
Expand Down Expand Up @@ -179,81 +180,158 @@ for _, strategy in helpers.each_strategy() do
end
end)

describe("without Content-Length", function()
it("works if size is lower than limit", function()
local body = string.rep("a", (TEST_SIZE * MB))
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test"
}
})
assert.res_status(200, res)
end)
describe("without Content-Length(chunked request body)", function()
describe("[request body size > nginx_http_client_body_buffer_size]", function()
it("works if size is lower than limit", function()
local str_len = TEST_SIZE * MB
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
assert.res_status(200, res)
end)

it("works if size is lower than limit and Expect header", function()
local body = string.rep("a", (TEST_SIZE * MB))
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Expect"] = "100-continue"
}
})
assert.res_status(200, res)
end)
it("works if size is lower than limit and Expect header", function()
local str_len = TEST_SIZE * MB
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Expect"] = "100-continue",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
assert.res_status(200, res)
end)

it("blocks if size is greater than limit", function()
local body = string.rep("a", (TEST_SIZE * MB) + 1)
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test"
}
})
local body = assert.res_status(413, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
it("blocks if size is greater than limit", function()
local str_len = (TEST_SIZE * MB) + 1
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
local body = assert.res_status(413, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
end)

it("blocks if size is greater than limit and Expect header", function()
local str_len = (TEST_SIZE * MB) + 1
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Expect"] = "100-continue",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
local body = assert.res_status(417, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
end)
end)

it("blocks if size is greater than limit and Expect header", function()
local body = string.rep("a", (TEST_SIZE * MB) + 1)
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit.test",
["Expect"] = "100-continue"
}
})
local body = assert.res_status(417, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
describe("[request body size < nginx_http_client_body_buffer_size]", function()
it("works if size is lower than limit", function()
local str_len = TEST_SIZE * KB
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit_kilobytes.test",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
assert.res_status(200, res)
end)

it("works if size is lower than limit and Expect header", function()
local str_len = TEST_SIZE * KB
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit_kilobytes.test",
["Expect"] = "100-continue",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
assert.res_status(200, res)
end)

it("blocks if size is greater than limit", function()
local str_len = (TEST_SIZE * KB) + 1
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit_kilobytes.test",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
local body = assert.res_status(413, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
end)

it("blocks if size is greater than limit and Expect header", function()
local str_len = (TEST_SIZE * KB) + 1
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = "limit_kilobytes.test",
["Expect"] = "100-continue",
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
local body = assert.res_status(417, res)
local json = cjson.decode(body)
assert.not_nil(json)
assert.matches("Request size limit exceeded", json.message)
end)
end)

for _, unit in ipairs(size_units) do
it("blocks if size is greater than limit when unit in " .. unit, function()
local body = string.rep("a", (TEST_SIZE * unit_multiplication_factor[unit]) + 1)
local str_len = (TEST_SIZE * unit_multiplication_factor[unit]) + 1
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = string.format("limit_%s.test", unit),
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
local body = assert.res_status(413, res)
Expand All @@ -265,14 +343,15 @@ for _, strategy in helpers.each_strategy() do

for _, unit in ipairs(size_units) do
it("works if size is less than limit when unit in " .. unit, function()
local body = string.rep("a", (TEST_SIZE * unit_multiplication_factor[unit]))
local str_len = (TEST_SIZE * unit_multiplication_factor[unit])
local body = string.format("%x", str_len) .. "\r\n" .. string.rep("a", str_len) .. "\r\n0\r\n\r\n"
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
body = body,
headers = {
["Host"] = string.format("limit_%s.test", unit),
["Transfer-Encoding"] = "chunked", -- lua-resty-http do not add content-length when client send chunked request body
}
})
assert.res_status(200, res)
Expand All @@ -284,7 +363,7 @@ for _, strategy in helpers.each_strategy() do
it("blocks if header is not provided", function()
local res = assert(proxy_client:request {
dont_add_content_length = true,
method = "GET", -- if POST, then lua-rsty-http adds content-length anyway
method = "GET", -- if POST, then lua-resty-http adds content-length anyway
path = "/request",
headers = {
["Host"] = "required.test",
Expand Down
Loading