diff --git a/analyze.go b/analyze.go index d453b3d6..e19c0d80 100644 --- a/analyze.go +++ b/analyze.go @@ -23,10 +23,11 @@ const ( // ngxConfTake7 = 0x00000080 // 7 args (currently unused). ngxConfBlock = 0x00000100 // followed by block ngxConfExpr = 0x00000200 // directive followed by expression in parentheses `()` - ngxConfFlag = 0x00000400 // 'on' or 'off' - ngxConfAny = 0x00000800 // >=0 args - ngxConf1More = 0x00001000 // >=1 args - ngxConf2More = 0x00002000 // >=2 args + + ngxConfFlag uint = 0x00000400 // 'on' or 'off' + ngxConfAny uint = 0x00000001 // Example value + ngxConf1More uint = 0x00000002 // Example value + ngxConf2More uint = 0x00000004 // Example value // some helpful argument style aliases. ngxConfTake12 = ngxConfTake1 | ngxConfTake2 @@ -182,7 +183,7 @@ func analyze(fname string, stmt *Directive, term string, ctx blockCtx, options * // use mask to check the directive's arguments //nolint:gocritic if ((mask>>len(stmt.Args)&1) != 0 && len(stmt.Args) <= 7) || // NOARGS to TAKE7 - ((mask&ngxConfFlag) != 0 && len(stmt.Args) == 1 && validFlag(stmt.Args[0])) || + ((mask&(ngxConfFlag)) != 0 && len(stmt.Args) == 1 && validFlag(stmt.Args[0])) || ((mask & ngxConfAny) != 0) || ((mask&ngxConf1More) != 0 && len(stmt.Args) >= 1) || ((mask&ngxConf2More) != 0 && len(stmt.Args) >= 2) { diff --git a/analyze_test.go b/analyze_test.go index 57b86f72..df4d86db 100644 --- a/analyze_test.go +++ b/analyze_test.go @@ -603,6 +603,43 @@ func TestAnalyze_nap_app_protect_enable(t *testing.T) { } } +func TestAnalyze_limit_req_zone(t *testing.T) { + t.Parallel() + testcases := map[string]struct { + stmt *Directive + ctx blockCtx + wantErr bool + }{ + "limit_req_zone ok http": { + &Directive{ + Directive: "limit_req_zone", + Args: []string{"$binary_remote_addr", "zone=one:10m", "rate=1r/s", "sync"}, + Line: 5, + }, + blockCtx{"http"}, + false, + }, + } + + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + err := analyze("nginx.conf", tc.stmt, ";", tc.ctx, &ParseOptions{ + DirectiveSources: []MatchFunc{NgxPlusLatestDirectivesMatchFn, AppProtectWAFv4DirectivesMatchFn}, + }) + + if !tc.wantErr && err != nil { + t.Fatal(err) + } + + if tc.wantErr && err == nil { + t.Fatal("expected error, got nil") + } + }) + } +} + //nolint:funlen func TestAnalyze_nap_app_protect_security_log_enable(t *testing.T) { t.Parallel() diff --git a/parse_test.go b/parse_test.go index 9db35559..d0628b40 100644 --- a/parse_test.go +++ b/parse_test.go @@ -2012,6 +2012,100 @@ var parseFixtures = []parseFixture{ }, }, }}, + {"limit-req-zone", "", ParseOptions{SingleFile: true}, Payload{ + Status: "ok", + Errors: []PayloadError{}, + Config: []Config{ + { + File: getTestConfigPath("limit-req-zone", "nginx.conf"), + Status: "ok", + Errors: []ConfigError{}, + Parsed: Directives{ + { + Directive: "user", + Args: []string{"nginx"}, + Line: 1, + Block: nil, + }, + { + Directive: "worker_processes", + Args: []string{"auto"}, + Line: 2, + Block: nil, + }, + { + Directive: "error_log", + Args: []string{"/var/log/nginx/error.log", "notice"}, + Line: 4, + Block: nil, + }, + { + Directive: "pid", + Args: []string{"/var/run/nginx.pid"}, + Line: 5, + Block: nil, + }, + { + Directive: "events", + Args: []string{}, + Line: 7, + Block: Directives{ + { + Directive: "worker_connections", + Args: []string{"1024"}, + Line: 8, + }, + }, + }, + { + Directive: "http", + Args: []string{}, + Line: 11, + Block: Directives{ + { + Directive: "include", + Args: []string{"/etc/nginx/mime.types"}, + Line: 12, + }, + { + Directive: "default_type", + Args: []string{"application/octet-stream"}, + Line: 13, + }, + { + Directive: "limit_req_zone", + Args: []string{"$binary_remote_addr", "zone=one:10m", "rate=1r/s", "sync"}, + Line: 15, + }, + { + Directive: "log_format", + Args: []string{"main", + "$remote_addr - $remote_user [$time_local] \"$request\" ", + "$status $body_bytes_sent \"$http_referer\" ", + "\"$http_user_agent\" \"$http_x_forwarded_for\"", + }, + Line: 17, + }, + { + Directive: "access_log", + Args: []string{"/var/log/nginx/access.log", "main"}, + Line: 21, + }, + { + Directive: "sendfile", + Args: []string{"on"}, + Line: 23, + }, { + Directive: "keepalive_timeout", + Args: []string{"65"}, + Line: 25, + }, + }, + }, + }, + }, + }, + }}, } func TestParse(t *testing.T) { diff --git a/testdata/configs/limit-req-zone/nginx.conf b/testdata/configs/limit-req-zone/nginx.conf new file mode 100644 index 00000000..c13aad1e --- /dev/null +++ b/testdata/configs/limit-req-zone/nginx.conf @@ -0,0 +1,26 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s sync; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + + keepalive_timeout 65; +} \ No newline at end of file