diff --git a/gixy/cli/main.py b/gixy/cli/main.py index ecef89c..5561f18 100644 --- a/gixy/cli/main.py +++ b/gixy/cli/main.py @@ -98,7 +98,7 @@ def main(): _init_logger(args.debug) path = os.path.expanduser(args.nginx_file) - if not os.path.isfile(path): + if path != '-' and not os.path.exists(path): sys.stderr.write('Please specify path to Nginx configuration.\n\n') parser.print_help() sys.exit(1) @@ -150,7 +150,13 @@ def main(): config.set_for(name, options) with Gixy(config=config) as yoda: - yoda.audit(path) + if path == '-': + with os.fdopen(sys.stdin.fileno(), mode='r') as fdata: + yoda.audit('', fdata, is_stdin=True) + else: + with open(path, mode='r') as fdata: + yoda.audit(path, fdata, is_stdin=False) + formatted = formatters()[config.output_format]().format(yoda) if args.output_file: with open(config.output_file, 'w') as f: diff --git a/gixy/core/manager.py b/gixy/core/manager.py index 2017469..1147382 100644 --- a/gixy/core/manager.py +++ b/gixy/core/manager.py @@ -1,25 +1,32 @@ +import os +import logging + import gixy from gixy.core.plugins_manager import PluginsManager from gixy.core.context import get_context, pop_context, push_context, purge_context from gixy.parser.nginx_parser import NginxParser from gixy.core.config import Config +LOG = logging.getLogger(__name__) + class Manager(object): def __init__(self, config=None): self.root = None - self.parser = None - self.auditor = None self.config = config or Config() + self.auditor = PluginsManager(config=self.config) self.stats = {gixy.severity.UNSPECIFIED: 0, gixy.severity.LOW: 0, gixy.severity.MEDIUM: 0, gixy.severity.HIGH: 0} - def audit(self, file_path): - self.auditor = PluginsManager(config=self.config) - self.parser = NginxParser(file_path, allow_includes=self.config.allow_includes) - self.root = self.parser.parse(file_path) + def audit(self, file_path, file_data, is_stdin=False): + LOG.debug("Audit config file: {fname}".format(fname=file_path)) + parser = NginxParser( + cwd=os.path.dirname(file_path) if not is_stdin else '', + allow_includes=self.config.allow_includes) + self.root = parser.parse(content=file_data.read(), path_info=file_path) + push_context(self.root) self._audit_recursive(self.root.children) diff --git a/gixy/parser/nginx_parser.py b/gixy/parser/nginx_parser.py index 7a30c26..ec9506b 100644 --- a/gixy/parser/nginx_parser.py +++ b/gixy/parser/nginx_parser.py @@ -12,26 +12,32 @@ LOG = logging.getLogger(__name__) class NginxParser(object): - def __init__(self, file_path, allow_includes=True): - self.base_file_path = file_path - self.cwd = os.path.dirname(file_path) + def __init__(self, cwd='', allow_includes=True): + self.cwd = cwd self.configs = {} self.is_dump = False self.allow_includes = allow_includes self.directives = {} + self.parser = raw_parser.RawParser() self._init_directives() - def parse(self, file_path, root=None): - LOG.debug("Parse file: {}".format(file_path)) + def parse_file(self, path, root=None): + LOG.debug("Parse file: {}".format(path)) + content = open(path).read() + return self.parse(content=content, root=root, path_info=path) + def parse(self, content, root=None, path_info=None): if not root: root = block.Root() try: - parser = raw_parser.RawParser() - parsed = parser.parse(file_path) + parsed = self.parser.parse(content) except ParseException as e: - LOG.error('Failed to parse config "{file}": {error}'.format(file=file_path, error=str(e))) + error_msg = 'char {char} (line:{line}, col:{col})'.format(char=e.loc, line=e.lineno, col=e.col) + if path_info: + LOG.error('Failed to parse config "{file}": {error}'.format(file=path_info, error=error_msg)) + else: + LOG.error('Failed to parse config: {error}'.format(error=error_msg)) return root if len(parsed) and parsed[0].getName() == 'file_delimiter': @@ -108,7 +114,7 @@ class NginxParser(object): for file_path in glob.iglob(path): include = block.IncludeBlock('include', [file_path]) parent.append(include) - self.parse(file_path, include) + self.parse_file(file_path, include) if not file_path: LOG.warning("File not found: {}".format(path)) diff --git a/gixy/parser/raw_parser.py b/gixy/parser/raw_parser.py index e2fdd59..84aa18e 100644 --- a/gixy/parser/raw_parser.py +++ b/gixy/parser/raw_parser.py @@ -25,17 +25,15 @@ class RawParser(object): """ def __init__(self): - self._script = None + self._if_fixer = re.compile(r'(if\s.+)\)\)(\s*\{)?$', flags=re.MULTILINE) - def parse(self, file_path): + def parse(self, data): """ Returns the parsed tree. """ # Temporary, dirty hack :( - content = open(file_path).read() - content = re.sub(r'(if\s.+)\)\)(\s*\{)?$', '\\1) )\\2', content, flags=re.MULTILINE) + content = self._if_fixer.sub('\\1) )\\2', data) return self.script.parseString(content, parseAll=True) - # return self.script.parseFile(file_path, parseAll=True) @cached_property def script(self): diff --git a/tests/directives/test_block.py b/tests/directives/test_block.py index 5ac7ae0..cd56e4c 100644 --- a/tests/directives/test_block.py +++ b/tests/directives/test_block.py @@ -1,17 +1,13 @@ from nose.tools import assert_equals, assert_is_instance, assert_is_not_none, assert_is_none, assert_true, assert_false -import mock -from six import StringIO -from six.moves import builtins from gixy.parser.nginx_parser import NginxParser from gixy.directives.block import * # TODO(buglloc): what about include block? + def _get_parsed(config): - with mock.patch('%s.open' % builtins.__name__) as mock_open: - mock_open.return_value = StringIO(config) - root = NginxParser('/foo/bar', allow_includes=False).parse('/foo/bar') - return root.children[0] + root = NginxParser(cwd='', allow_includes=False).parse(config) + return root.children[0] def test_block(): diff --git a/tests/directives/test_directive.py b/tests/directives/test_directive.py index e846e2d..612c3f9 100644 --- a/tests/directives/test_directive.py +++ b/tests/directives/test_directive.py @@ -1,15 +1,11 @@ from nose.tools import assert_equals, assert_is_instance, assert_false, assert_true -import mock -from six import StringIO -from six.moves import builtins from gixy.parser.nginx_parser import NginxParser from gixy.directives.directive import * def _get_parsed(config): - with mock.patch('%s.open' % builtins.__name__) as mock_open: - mock_open.return_value = StringIO(config) - return NginxParser('/foo/bar', allow_includes=False).parse('/foo/bar').children[0] + root = NginxParser(cwd='', allow_includes=False).parse(config) + return root.children[0] def test_directive(): diff --git a/tests/parser/test_nginx_parser.py b/tests/parser/test_nginx_parser.py index 2609df2..782f702 100644 --- a/tests/parser/test_nginx_parser.py +++ b/tests/parser/test_nginx_parser.py @@ -1,16 +1,11 @@ from nose.tools import assert_is_instance, assert_equal -import mock -from six import StringIO -from six.moves import builtins from gixy.parser.nginx_parser import NginxParser from gixy.directives.directive import * from gixy.directives.block import * def _parse(config): - with mock.patch('%s.open' % builtins.__name__) as mock_open: - mock_open.return_value = StringIO(config) - return NginxParser('/foo/bar', allow_includes=False).parse('/foo/bar') + return NginxParser(cwd='', allow_includes=False).parse(config) def test_directive(): diff --git a/tests/parser/test_raw_parser.py b/tests/parser/test_raw_parser.py index e4d55f7..ddbbc6c 100644 --- a/tests/parser/test_raw_parser.py +++ b/tests/parser/test_raw_parser.py @@ -493,7 +493,5 @@ add_header X-Blank-Comment blank; def assert_config(config, expected): - with mock.patch('%s.open' % builtins.__name__) as mock_open: - mock_open.return_value = StringIO(config) - actual = RawParser().parse('/foo/bar') - assert_equals(actual.asList(), expected) + actual = RawParser().parse(config) + assert_equals(actual.asList(), expected) diff --git a/tests/plugins/test_simply.py b/tests/plugins/test_simply.py index e93fb65..6de1c5c 100644 --- a/tests/plugins/test_simply.py +++ b/tests/plugins/test_simply.py @@ -81,7 +81,7 @@ def yoda_provider(plugin, plugin_options=None): def check_configuration(plugin, config_path, test_config): plugin_options = parse_plugin_options(config_path) with yoda_provider(plugin, plugin_options) as yoda: - yoda.audit(config_path) + yoda.audit(config_path, open(config_path, mode='r')) results = RawFormatter().format(yoda) assert_equals(len(results), 1, 'Should have one report') @@ -102,7 +102,7 @@ def check_configuration(plugin, config_path, test_config): def check_configuration_fp(plugin, config_path, test_config): with yoda_provider(plugin) as yoda: - yoda.audit(config_path) + yoda.audit(config_path, open(config_path, mode='r')) results = RawFormatter().format(yoda) assert_equals(len(results), 0,