Skip to content
Open
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
22 changes: 14 additions & 8 deletions src/lua_resty_netacea.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ local function buildResult(idType, mitigationType, captchaState)
}
end

local function serveCaptcha(captchaBody)
ngx.status = ngx.HTTP_FORBIDDEN
local function serveCaptcha(statusCode, captchaBody)
ngx.status = statusCode
ngx.header["content-type"] = "text/html"
ngx.header["Cache-Control"] = "max-age=0, no-cache, no-store, must-revalidate"
ngx.print(captchaBody)
return ngx.exit(ngx.HTTP_OK)
end

local function serveBlock()
ngx.status = ngx.HTTP_FORBIDDEN;
local function serveBlock(statusCode, blockBody)
ngx.status = statusCode;
ngx.header["Cache-Control"] = "max-age=0, no-cache, no-store, must-revalidate"
ngx.print("403 Forbidden");
return ngx.exit(ngx.HTTP_FORBIDDEN);
ngx.print(blockBody);
return ngx.exit(statusCode);
end

local function buildRandomString(length)
Expand Down Expand Up @@ -83,6 +83,12 @@ function _N:new(options)
if not self.secretKey or self.secretKey == '' then
self.mitigationEnabled = false
end
-- mitigate:optional:captchaStatusCode
self.captchaStatusCode = options.captchaStatusCode or ngx.HTTP_FORBIDDEN
-- mitigate:optional:blockStatusCode
self.blockStatusCode = options.blockStatusCode or ngx.HTTP_FORBIDDEN
-- mitigate:optional:blockBody
self.blockBody = options.blockBody or '403 Forbidden'
-- global:optional:realIpHeader
self.realIpHeader = options.realIpHeader or ''
-- global:optional:userIdKey
Expand Down Expand Up @@ -429,10 +435,10 @@ function _N:getBestMitigation(mitigationType, captchaState, res)
if (captchaState == _N.captchaStates.COOKIEPASS) then return nil end

if (mitigationType == _N.mitigationTypes.BLOCKED and captchaState == _N.captchaStates.SERVE and res ~= nil) then
return serveCaptcha(res.body)
return serveCaptcha(self.captchaStatusCode, res.body)
end

return serveBlock()
return serveBlock(self.blockStatusCode, self.blockBody)
end

function _N:setBcType(match, mitigate, captcha)
Expand Down
85 changes: 85 additions & 0 deletions test/lua_resty_netacea.test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,45 @@ insulate("lua_resty_netacea.lua", function()
assert.spy(logFunc).was.called()
end)

it('allows customisation of response if mitata cookie is BLOCK', function()
local expected_block_status_code = 499
local expected_block_body = 'Blocked'

local req_spy = setHttpResponse('-', nil, 'error')

local netacea = (require 'lua_resty_netacea'):new({
ingestEndpoint = '',
mitigationEndpoint = mit_svc_url,
apiKey = mit_svc_api_key,
secretKey = mit_svc_secret,
realIpHeader = '',
ingestEnabled = false,
mitigationEnabled = true,
mitigationType = 'MITIGATE',
blockStatusCode = expected_block_status_code,
blockBody = expected_block_body
})

local mit = netacea.idTypes.IP .. netacea.mitigationTypes.BLOCKED .. netacea.captchaStates.NONE
ngx.var.cookie__mitata = build_mitata_cookie(ngx.time() + 20, generate_uid(), mit, mit_svc_secret)

local logFunc = spy.new(function(res)
assert(res.idType == netacea.idTypes.IP)
assert(res.mitigationType == netacea.mitigationTypes.BLOCKED)
assert(res.captchaState == netacea.captchaStates.NONE)
end)

netacea:run(logFunc)

assert.spy(req_spy).was.not_called()
assert(ngx.status == expected_block_status_code)
assert.spy(ngx.print).was.called_with(expected_block_body)

assert(ngx.header['Cache-Control'] == 'max-age=0, no-cache, no-store, must-revalidate')
assert.spy(ngx.exit).was.called()
assert.spy(logFunc).was.called()
end)

it('forwards to mit service if mitata cookie is CAPTCHA SERVE', function()
local expected_captcha_body = 'some captcha body'
local netacea = require 'lua_resty_netacea'
Expand Down Expand Up @@ -487,6 +526,52 @@ insulate("lua_resty_netacea.lua", function()
assert.spy(logFunc).was.called()
end)

it('it allows custom HTTP status code if mitata cookie is CAPTCHA SERVE', function()
local expected_captcha_status_code = 498
local expected_captcha_body = 'some captcha body'
local netacea = require 'lua_resty_netacea'
local req_spy = setHttpResponse(mit_svc_url, {
headers = {
['x-netacea-match'] = netacea.idTypes.IP,
['x-netacea-mitigate'] = netacea.mitigationTypes.BLOCKED,
['x-netacea-captcha'] = netacea.captchaStates.SERVE
},
status = 200,
body = expected_captcha_body
}, nil)

package.loaded['lua_resty_netacea'] = nil
netacea = (require 'lua_resty_netacea'):new({
ingestEndpoint = '',
mitigationEndpoint = mit_svc_url,
apiKey = mit_svc_api_key,
secretKey = mit_svc_secret,
realIpHeader = '',
ingestEnabled = false,
mitigationEnabled = true,
mitigationType = 'MITIGATE',
captchaStatusCode = expected_captcha_status_code
})

local mit = netacea.idTypes.IP .. netacea.mitigationTypes.BLOCKED .. netacea.captchaStates.SERVE
ngx.var.cookie__mitata = build_mitata_cookie(ngx.time() + 20, generate_uid(), mit, mit_svc_secret)

local logFunc = spy.new(function(res)
assert(res.idType == netacea.idTypes.IP)
assert(res.mitigationType == netacea.mitigationTypes.BLOCKED)
assert(res.captchaState == netacea.captchaStates.SERVE)
end)

netacea:run(logFunc)

assert.spy(req_spy).was.called()
assert(ngx.status == expected_captcha_status_code)
assert.spy(ngx.print).was.called_with(expected_captcha_body)
assert(ngx.header['Cache-Control'] == 'max-age=0, no-cache, no-store, must-revalidate')
assert.spy(ngx.exit).was.called()
assert.spy(logFunc).was.called()
end)

it('serves captcha if client is mitigated', function()
local expected_captcha_body = 'some captcha body'

Expand Down