diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5d9df55 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,57 @@ +# Byte-compiled / optimized / DLL files +**/__pycache__/ +**/*.py[cod] + +# C extensions +***/*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +cover + +# Translations +**/*.mo +**/*.pot + +# PyBuilder +target/ + +venv/ +venv3/ +.idea/ + +# 100% unnecessary for docker image +.* +*.md +docs +rpm +Dockerfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..abd7688 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +end_of_file = lf +insert_final_newline = true + +[*.{py,j2}] +charset = utf-8 + +[*.py] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[.travis.yml] +indent_style = space +indent_size = 2 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ef7066e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM python:2.7-alpine + +ADD . /src + +WORKDIR /src + +RUN python2 setup.py install + +ENTRYPOINT ["gixy"] diff --git a/docs/en/plugins/addheaderredefinition.md b/docs/en/plugins/addheaderredefinition.md index 8eef0c4..5fe5742 100644 --- a/docs/en/plugins/addheaderredefinition.md +++ b/docs/en/plugins/addheaderredefinition.md @@ -1,6 +1,6 @@ # [add_header_redefinition] Redefining of response headers by "add_header" directive -Unfortunately, many people doesn't know how the inheritance of directives works. Most often this leads to misuse of the `add_header` directive while trying to add a new response header on the nested level. +Unfortunately, many people don't know how the inheritance of directives works. Most often this leads to misuse of the `add_header` directive while trying to add a new response header on the nested level. This feature is mentioned in Nginx [docs](http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header): > There could be several `add_header` directives. These directives are inherited from the previous level if and only if there are no `add_header` directives defined on the current level. diff --git a/docs/en/plugins/hostspoofing.md b/docs/en/plugins/hostspoofing.md index 1447f0b..a751ae3 100644 --- a/docs/en/plugins/hostspoofing.md +++ b/docs/en/plugins/hostspoofing.md @@ -10,7 +10,7 @@ Spoofing of this header, may leads to a variety of problems, from phishing to SS Most of the time it's a result of using `$http_host` variable instead of `$host`. And they are quite different: - * `$http` - host in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request; + * `$host` - host in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request; * `$http_host` - "Host" request header. Config sample: @@ -29,4 +29,4 @@ Luckily, all is quite obvious: ## Additional info * [Host of Troubles Vulnerabilities](https://hostoftroubles.com/) - * [Practical HTTP Host header attacks](http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html) \ No newline at end of file + * [Practical HTTP Host header attacks](http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html) diff --git a/gixy/directives/block.py b/gixy/directives/block.py index e55b725..0dd2651 100644 --- a/gixy/directives/block.py +++ b/gixy/directives/block.py @@ -141,7 +141,7 @@ class IfBlock(Block): # if ($request_method = POST) self.variable, self.operand, self.value = args else: - raise Exception('Unknown "if" definition') + raise Exception('Unknown "if" definition, args: {}'.format(repr(args))) def __str__(self): return '{} ({}) {{'.format(self.name, ' '.join(self.args)) diff --git a/gixy/parser/raw_parser.py b/gixy/parser/raw_parser.py index 84aa18e..3802125 100644 --- a/gixy/parser/raw_parser.py +++ b/gixy/parser/raw_parser.py @@ -1,4 +1,3 @@ -import re import logging from cached_property import cached_property @@ -31,9 +30,7 @@ class RawParser(object): """ Returns the parsed tree. """ - # Temporary, dirty hack :( - content = self._if_fixer.sub('\\1) )\\2', data) - return self.script.parseString(content, parseAll=True) + return self.script.parseString(data, parseAll=True) @cached_property def script(self): @@ -59,10 +56,15 @@ class RawParser(object): Keyword("=") | Keyword("~*") | Keyword("~") | (Literal("-") + (Literal("f") | Literal("d") | Literal("e") | Literal("x"))))) - condition = ( + condition_body = ( (if_modifier + Optional(space) + value) | (variable + Optional(space + if_modifier + Optional(space) + value)) ) + # This ugly workaround needed to parse unquoted regex with nested parentheses + # pyparsing.nestedExpr doesn't work in some rare cases like: ($http_user_agent ~* \( ) + # so we capture all content between parentheses and then parse it:) + # TODO(buglloc): may be use something better? + condition = Regex(r'\(.*\)').setParseAction(lambda s, l, t: condition_body.parseString(t[0][1:-1])) # rules include = ( @@ -112,9 +114,7 @@ class RawParser(object): if_block << ( Keyword("if") + - Suppress("(") + Group(condition) + - Suppress(")") + Group( left_bracket + Optional(sub_block) + diff --git a/tests/parser/test_raw_parser.py b/tests/parser/test_raw_parser.py index ddbbc6c..05a78d7 100644 --- a/tests/parser/test_raw_parser.py +++ b/tests/parser/test_raw_parser.py @@ -225,11 +225,14 @@ if (!-e "/var/data/$dataset") { return 503; } -if ($https_or_slb = (by_slb|https)) { +if ($https_or_slb = (by_\(sl\)b|https)) { } if ($host ~* (lori|rage2)\.yandex\.(ru|ua|com|com\.tr)) { set $x_frame_options ALLOW; +} + +if ($request_filename ~* ^.*?/(\d+_)([^/]+)$) { } ''' @@ -253,11 +256,13 @@ if ($host ~* (lori|rage2)\.yandex\.(ru|ua|com|com\.tr)) { ['if', ['!-e', '/var/data/$dataset'], [ ['return', '503'] ]], - ['if', ['$https_or_slb', '=', '(by_slb|https)'], [ + ['if', ['$https_or_slb', '=', '(by_\(sl\)b|https)'], [ ]], ['if', ['$host', '~*', '(lori|rage2)\.yandex\.(ru|ua|com|com\.tr)'], [ ['set', '$x_frame_options', 'ALLOW'] ]], + ['if', ['$request_filename', '~*', '^.*?/(\d+_)([^/]+)$'], [ + ]] ] assert_config(config, expected)