diff --git a/CHANGELOG.md b/CHANGELOG.md index 752b641ccbc0..6f447c7fbd5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,12 @@ [#9626](https://github.com/Kong/kong/pull/9626) +#### Plugins + +- **Response-Transformer**: Fix the bug that Response-Transformer plugin + breaks when receiving an unexcepted boy. + [#9463](https://github.com/Kong/kong/pull/9463) + ## [3.0.0] > Released 2022/09/12 diff --git a/kong/plugins/response-transformer/body_transformer.lua b/kong/plugins/response-transformer/body_transformer.lua index 32694f616a7b..f89ca07dc41e 100644 --- a/kong/plugins/response-transformer/body_transformer.lua +++ b/kong/plugins/response-transformer/body_transformer.lua @@ -1,4 +1,5 @@ local cjson = require("cjson.safe").new() +local cjson_decode = cjson.decode local insert = table.insert @@ -8,6 +9,8 @@ local sub = string.sub local gsub = string.gsub local match = string.match local lower = string.lower +local tonumber = tonumber +local pcall = pcall cjson.decode_array_with_array_mt(true) @@ -39,9 +42,12 @@ local function cast_value(value, value_type) end -local function read_json_body(body) +local function parse_json(body) if body then - return cjson.decode(body) + local ok, res = pcall(cjson_decode, body) + if ok then + return res + end end end @@ -90,9 +96,9 @@ end function _M.transform_json_body(conf, buffered_data) - local json_body = read_json_body(buffered_data) + local json_body = parse_json(buffered_data) if json_body == nil then - return + return nil, "failed parsing json body" end -- remove key:value to body diff --git a/kong/plugins/response-transformer/handler.lua b/kong/plugins/response-transformer/handler.lua index 240501953b7f..ea990168d934 100644 --- a/kong/plugins/response-transformer/handler.lua +++ b/kong/plugins/response-transformer/handler.lua @@ -20,6 +20,7 @@ end function ResponseTransformerHandler:body_filter(conf) + if not is_body_transform_set(conf) or not is_json_body(kong.response.get_header("Content-Type")) then @@ -27,9 +28,13 @@ function ResponseTransformerHandler:body_filter(conf) end local body = kong.response.get_raw_body() - if body then - return kong.response.set_raw_body(body_transformer.transform_json_body(conf, body)) + + local json_body, err = body_transformer.transform_json_body(conf, body) + if err then + kong.log.warn("body transform failed: " .. err) + return end + return kong.response.set_raw_body(json_body) end diff --git a/spec/03-plugins/15-response-transformer/02-body_transformer_spec.lua b/spec/03-plugins/15-response-transformer/02-body_transformer_spec.lua index 41d7df53b1c7..1392f5e8ec05 100644 --- a/spec/03-plugins/15-response-transformer/02-body_transformer_spec.lua +++ b/spec/03-plugins/15-response-transformer/02-body_transformer_spec.lua @@ -322,4 +322,68 @@ describe("Plugin: response-transformer", function() assert.are.same(body, result) end) end) + + describe("handle unexpected body type", function() + -- Related to issue https://github.com/Kong/kong/issues/9461 + + local old_kong, handler + + lazy_setup(function() + old_kong = _G.kong + _G.kong = { + response = { + get_header = function(header) + if header == "Content-Type" then + return "application/json" + end + end, + get_raw_body = function() + return "not a json value" + end, + set_raw_body = function() end + }, + log = { + warn = function() end + } + } + + -- force module reload to use mock `_G.kong` + package.loaded["kong.plugins.response-transformer.handler"] = nil + handler = require("kong.plugins.response-transformer.handler") + end) + + lazy_teardown(function() + _G.kong = old_kong + end) + + it("gracefully fails transforming invalid json body", function() + local conf = { + remove = { + headers = {}, + json = { "foo" } + }, + add = { + headers = {}, + json = {}, + }, + append = { + headers = {}, + json = {}, + }, + replace = { + headers = {}, + json = {}, + }, + } + + local spy_response_get_header = spy.on(kong.response, "get_header") + local spy_response_get_raw_body = spy.on(kong.response, "get_raw_body") + local spy_response_set_raw_body = spy.on(kong.response, "set_raw_body") + + assert.is_nil(handler:body_filter(conf)) + assert.spy(spy_response_get_header).was_called_with("Content-Type") + assert.spy(spy_response_get_raw_body).was_called() + assert.spy(spy_response_set_raw_body).was_not_called() + end) + end) end)