Skip to content

Commit

Permalink
Add status nondisruptive rule
Browse files Browse the repository at this point in the history
This overrides the deny_status option on a per-rule basis.
  • Loading branch information
p0pr0ck5 committed Sep 8, 2016
1 parent c7e531b commit 6c24b72
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/resty/waf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ local function _process_rule(self, rule, collections, ctx)

ctx.id = id

ctx.rule_status = nil

for k, v in ipairs(vars) do
local collection, var
var = vars[k]
Expand Down
7 changes: 6 additions & 1 deletion lib/resty/waf/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ _M.disruptive_lookup = {
DENY = function(waf, ctx)
logger.log(waf, "Rule action was DENY, so telling nginx to quit")
if (waf._mode == "ACTIVE") then
ngx.exit(waf._deny_status)
ngx.exit(ctx.rule_status or waf._deny_status)
end
end,
DROP = function(waf, ctx)
Expand Down Expand Up @@ -72,6 +72,11 @@ _M.nondisruptive_lookup = {

ngx.sleep(time)
end,
status = function(waf, status, ctx)
logger.log(waf, "Overriding status from " .. waf._deny_status .. " to " .. status)

ctx.rule_status = status
end
}

return _M
123 changes: 123 additions & 0 deletions t/acceptance/actions/03_status.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use Test::Nginx::Socket::Lua;

repeat_each(3);
plan tests => repeat_each() * 4 * blocks();

no_shuffle();
run_tests();

__DATA__

=== TEST 1: No status nondisruptive action uses waf._deny_status
--- http_config
init_by_lua_block{
if (os.getenv("LRW_COVERAGE")) then
runner = require "luacov.runner"
runner.tick = true
runner.init({savestepsize = 110})
jit.off()
end
}
--- config
location /t {
access_by_lua '
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
waf:set_option("add_ruleset_string", "10100", [=[{"access":[{"actions":{"disrupt":"DENY"},"id":"12345","operator":"REGEX","pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
waf:exec()
ngx.log(ngx.INFO, "We should not see this")
';

content_by_lua 'ngx.exit(ngx.HTTP_OK)';
}
--- request
GET /t?a=foo
--- error_code: 403
--- error_log
Rule action was DENY, so telling nginx to quit
--- no_error_log
[error]
We should not see this

=== TEST 2: status nondisruptive action overrides waf._deny_status
--- http_config
init_by_lua_block{
if (os.getenv("LRW_COVERAGE")) then
runner = require "luacov.runner"
runner.tick = true
runner.init({savestepsize = 110})
jit.off()
end
}
--- config
location /t {
access_by_lua '
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
waf:set_option("add_ruleset_string", "10100", [=[{"access":[{"actions":{"disrupt":"DENY","nondisrupt":[{"action":"status","data":404}]},"id":"12345","operator":"REGEX","pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
waf:exec()
';

content_by_lua 'ngx.exit(ngx.HTTP_OK)';
}
--- request
GET /t?a=foo
--- error_code: 404
--- error_log
Rule action was DENY, so telling nginx to quit
Overriding status from 403 to 404
--- no_error_log
[error]

=== TEST 3: ctx.rule_status is reset every rule
--- http_config
init_by_lua_block{
if (os.getenv("LRW_COVERAGE")) then
runner = require "luacov.runner"
runner.tick = true
runner.init({savestepsize = 110})
jit.off()
end
}
--- config
location /s {
access_by_lua '
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
waf:set_option("add_ruleset_string", "10200", [=[{"access":[{"actions":{"disrupt":"DENY"},"id":"12345","operator":"REGEX","pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
waf:exec()
';

content_by_lua 'ngx.exit(ngx.HTTP_OK)';
}

location /t {
access_by_lua '
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
waf:set_option("add_ruleset_string", "10100", [=[{"access":[{"actions":{"disrupt":"DENY","nondisrupt":[{"action":"status","data":404}]},"id":"12345","operator":"REGEX","pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
waf:exec()
';

content_by_lua 'ngx.exit(ngx.HTTP_OK)';
}
--- request eval
["GET /t?a=foo", "GET /s?a=foo"]
--- error_code eval
[404, 403]
--- no_error_log
[error]

26 changes: 26 additions & 0 deletions t/translate/15_translate_actions.t
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,32 @@ warning_like
'warn when setvar sets not value, but does not prepend !'
;

$translation = {};
translate_actions(
{
actions => [
{
action => 'status',
value => '500',
}
]
},
$translation,
undef
);
is_deeply(
$translation,
{
actions => {
nondisrupt => [ {
action => 'status',
data => 500,
} ]
}
},
'translate status'
);

$translation = {};
translate_actions(
{
Expand Down
48 changes: 48 additions & 0 deletions t/unit/actions/nondisruptive/06_status.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use Test::Nginx::Socket::Lua;

repeat_each(3);
plan tests => repeat_each() * 4 * blocks();

no_shuffle();
run_tests();

__DATA__

=== TEST 1: status sets ctx.rule_status
--- http_config
init_by_lua_block{
if (os.getenv("LRW_COVERAGE")) then
runner = require "luacov.runner"
runner.tick = true
runner.init({savestepsize = 1})
jit.off()
end
}
--- config
location /t {
content_by_lua '
local actions = require "resty.waf.actions"
local ctx = {}
local new_status = ngx.HTTP_NOT_FOUND
actions.nondisruptive_lookup["status"](
{ _debug = true, _debug_log_level = ngx.INFO, _deny_status = ngx.HTTP_FORBIDDEN },
new_status,
ctx
)
ngx.say(ctx.rule_status)
';
}
--- request
GET /t
--- error_code: 200
--- response_body
404
--- error_log
Overriding status from 403 to 404
--- no_error_log
[error]

6 changes: 6 additions & 0 deletions tools/Modsec2LRW.pm
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,12 @@ sub translate_actions {
action => 'setvar',
data => $setvar
};
} elsif ($key eq 'status') {
push @{$translation->{actions}->{nondisrupt}},
{
action => $key,
data => $value += 0,
};
} elsif ($key eq 'pause') {
my $time = $value / 1000; # pause:n is given in ms, ngx.sleep takes its arg as seconds

Expand Down

0 comments on commit 6c24b72

Please sign in to comment.