diff --git a/src/lua_resty_netacea.lua b/src/lua_resty_netacea.lua index ffa3829..93fd96d 100644 --- a/src/lua_resty_netacea.lua +++ b/src/lua_resty_netacea.lua @@ -51,6 +51,31 @@ local function serveCaptchaFailOpen(body, options) return true end +local function readRequestBody() + ngx.req.read_body() + + local body = ngx.req.get_body_data() + if body ~= nil then + return body + end + + local body_file = ngx.req.get_body_file() + if not body_file then + return nil + end + + local file, err = io.open(body_file, "rb") + if not file then + ngx.log(ngx.WARN, "NETACEA CAPTCHA - unable to read request body file: ", err) + return nil + end + + local data = file:read("*a") + file:close() + + return data +end + function _N:new(options) local n = {} setmetatable(n, self) @@ -257,8 +282,7 @@ end function _N:handleCaptcha() self:handleSession() - ngx.req.read_body() - local captcha_data = ngx.req.get_body_data() + local captcha_data = readRequestBody() local protector_result = self.protectorClient:validateCaptcha(captcha_data) ngx.ctx.NetaceaState.protector_result = protector_result ngx.ctx.NetaceaState.grace_period = -1000 diff --git a/test/lua_resty_netacea_spec.lua b/test/lua_resty_netacea_spec.lua index a0141fe..e0c3635 100644 --- a/test/lua_resty_netacea_spec.lua +++ b/test/lua_resty_netacea_spec.lua @@ -29,6 +29,7 @@ insulate("lua_resty_netacea", function() req = { read_body = spy.new(function() end), get_body_data = spy.new(function() return "captcha-response" end), + get_body_file = spy.new(function() return nil end), set_header = spy.new(function() end) }, DEBUG = 7, @@ -926,6 +927,72 @@ insulate("lua_resty_netacea", function() assert.spy(ngx_mock.print).was.called_with("Captcha OK") assert.spy(ngx_mock.exit).was.called_with(200) end) + + it("should read captcha request bodies from the temporary file when needed", function() + local body_file = os.tmpname() + local file = assert(io.open(body_file, "wb")) + file:write("captcha-from-file") + file:close() + + ngx_mock.req.get_body_data = spy.new(function() + return nil + end) + ngx_mock.req.get_body_file = spy.new(function() + return body_file + end) + + local captured_body + protector_client_instance.validateCaptcha = spy.new(function(_, body) + captured_body = body + return { + match = "1", + mitigate = "1", + captcha = "2", + exit_status = 200, + captcha_cookie = nil, + response = { + body = "Captcha OK" + } + } + end) + + local netacea = new_mitigation_enabled_netacea() + + netacea:handleCaptcha() + + assert.are.equal("captcha-from-file", captured_body) + os.remove(body_file) + end) + + it("should return nil captcha body when the body file cannot be read", function() + ngx_mock.req.get_body_data = spy.new(function() + return nil + end) + ngx_mock.req.get_body_file = spy.new(function() + return "/tmp/definitely-not-a-real-body-file" + end) + + local captured_body + protector_client_instance.validateCaptcha = spy.new(function(_, body) + captured_body = body + return { + match = "1", + mitigate = "1", + captcha = "2", + exit_status = 200, + captcha_cookie = nil, + response = { + body = "Captcha OK" + } + } + end) + + local netacea = new_mitigation_enabled_netacea() + + netacea:handleCaptcha() + + assert.is_nil(captured_body) + end) end) end) end)