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) push_context(self.root) self._audit_recursive(self.root.children)
def __init__(self, config=None): self.root = 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}
class Manager(object): def __init__(self, config=None): self.root = None self.parser = None self.auditor = None self.config = config or 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) push_context(self.root) self._audit_recursive(self.root.children) def get_results(self): for plugin in self.auditor.plugins: if plugin.issues: self.stats[plugin.severity] += len(plugin.issues) yield plugin def _audit_recursive(self, tree): for directive in tree: self._update_variables(directive) self.auditor.audit(directive) if directive.is_block: if directive.self_context: push_context(directive) self._audit_recursive(directive.children) if directive.self_context: pop_context() def _update_variables(self, directive): # TODO(buglloc): finish him! if not directive.provide_variables: return context = get_context() for var in directive.variables: if var.name == 0: # All regexps must clean indexed variables context.clear_index_vars() context.add_var(var.name, var) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): purge_context()
def _get_cli_parser(): parser = create_parser() parser.add_argument('nginx_files', nargs='*', type=str, default=['/etc/nginx/nginx.conf'], metavar='nginx.conf', help='Path to nginx.conf, e.g. /etc/nginx/nginx.conf') parser.add_argument( '-v', '--version', action='version', version='Gixy v{0}'.format(gixy.version)) parser.add_argument( '-l', '--level', dest='level', action='count', default=0, help='Report issues of a given severity level or higher (-l for LOW, -ll for MEDIUM, -lll for HIGH)') default_formatter = 'console' if sys.stdout.isatty() else 'text' available_formatters = formatters().keys() parser.add_argument( '-f', '--format', dest='output_format', choices=available_formatters, default=default_formatter, type=str, help='Specify output format') parser.add_argument( '-o', '--output', dest='output_file', type=str, help='Write report to file') parser.add_argument( '-d', '--debug', dest='debug', action='store_true', default=False, help='Turn on debug mode') parser.add_argument( '--tests', dest='tests', type=str, help='Comma-separated list of tests to run') parser.add_argument( '--skips', dest='skips', type=str, help='Comma-separated list of tests to skip') parser.add_argument( '--disable-includes', dest='disable_includes', action='store_true', default=False, help='Disable "include" directive processing') group = parser.add_argument_group('plugins options') for plugin_cls in PluginsManager().plugins_classes: name = plugin_cls.__name__ if not plugin_cls.options: continue options = copy.deepcopy(plugin_cls.options) for opt_key, opt_val in options.items(): option_name = '--{plugin}-{key}'.format(plugin=name, key=opt_key).replace('_', '-') dst_name = '{plugin}:{key}'.format(plugin=name, key=opt_key) opt_type = str if isinstance(opt_val, (tuple, list, set)) else type(opt_val) group.add_argument( option_name, metavar=opt_key, dest=dst_name, type=opt_type, help=_create_plugin_help(opt_val) ) return parser
def format_help(self): manager = PluginsManager() help_message = super(GixyHelpFormatter, self).format_help() if 'plugins options:' in help_message: # Print available blugins _only_ if we prints options for it plugins = '\n'.join('\t' + plugin.__name__ for plugin in manager.plugins_classes) help_message = '{orig}\n\navailable plugins:\n{plugins}\n'.format( orig=help_message, plugins=plugins) return help_message
def test_from_config(): tested_plugins = set() tested_fp_plugins = set() conf_dir = path.join(path.dirname(__file__), 'simply') for plugin in os.listdir(conf_dir): if plugin in ('.', '..'): continue plugin_path = path.join(conf_dir, plugin) if not path.isdir(plugin_path): continue config = {} if path.exists(path.join(plugin_path, 'config.json')): with open(path.join(plugin_path, 'config.json'), 'r') as file: config = json.loads(file.read()) for test_case in os.listdir(plugin_path): if not test_case.endswith('.conf'): continue config_path = path.join(plugin_path, test_case) if not test_case.endswith('_fp.conf'): # Not False Positive test tested_plugins.add(plugin) test_func = check_configuration else: tested_fp_plugins.add(plugin) test_func = check_configuration_fp yield test_func, plugin, config_path, config manager = PluginsManager() for plugin in manager.plugins: plugin = plugin.name assert_true( plugin in tested_plugins, 'Plugin {name!r} should have at least one simple test config'. format(name=plugin)) assert_true( plugin in tested_fp_plugins, 'Plugin {name!r} should have at least one simple test config with false positive' .format(name=plugin))
def main(): parser = _get_cli_parser() args = parser.parse_args() _init_logger(args.debug) if len(args.nginx_files) == 1 and args.nginx_files[0] != '-': path = os.path.expanduser(args.nginx_files[0]) if not os.path.exists(path): sys.stderr.write('File {path!r} was not found.\nPlease specify correct path to configuration.\n'.format( path=path)) sys.exit(1) try: severity = gixy.severity.ALL[args.level] except IndexError: sys.stderr.write('Too high level filtering. Maximum level: -{0}\n'.format('l' * (len(gixy.severity.ALL) - 1))) sys.exit(1) if args.tests: tests = [x.strip() for x in args.tests.split(',')] else: tests = None if args.skips: skips = [x.strip() for x in args.skips.split(',')] else: skips = None config = Config( severity=severity, output_format=args.output_format, output_file=args.output_file, plugins=tests, skips=skips, allow_includes=not args.disable_includes ) for plugin_cls in PluginsManager().plugins_classes: name = plugin_cls.__name__ options = copy.deepcopy(plugin_cls.options) for opt_key, opt_val in options.items(): option_name = '{name}:{key}'.format(name=name, key=opt_key) if option_name not in args: continue val = getattr(args, option_name) if val is None: continue if isinstance(opt_val, tuple): val = tuple([x.strip() for x in val.split(',')]) elif isinstance(opt_val, set): val = set([x.strip() for x in val.split(',')]) elif isinstance(opt_val, list): val = [x.strip() for x in val.split(',')] options[opt_key] = val config.set_for(name, options) formatter = formatters()[config.output_format]() failed = False for input_path in args.nginx_files: path = os.path.abspath(os.path.expanduser(input_path)) if not os.path.exists(path): LOG.error('File %s was not found', path) continue with Gixy(config=config) as yoda: if path == '-': with os.fdopen(sys.stdin.fileno(), 'rb') as fdata: yoda.audit('<stdin>', fdata, is_stdin=True) else: with open(path, mode='rb') as fdata: yoda.audit(path, fdata, is_stdin=False) formatter.feed(path, yoda) failed = failed or sum(yoda.stats.values()) > 0 if args.output_file: with open(config.output_file, 'w') as f: f.write(formatter.flush()) else: print(formatter.flush()) if failed: # If something found - exit code must be 1, otherwise 0 sys.exit(1) sys.exit(0)
def __init__(self, config=None): self.root = None self.config = config or Config() self.auditor = PluginsManager(config=self.config)
class Manager(object): def __init__(self, config=None): self.root = None self.config = config or Config() self.auditor = PluginsManager(config=self.config) 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) @property def results(self): for plugin in self.auditor.plugins: if plugin.issues: yield plugin @property def stats(self): stats = dict.fromkeys(gixy.severity.ALL, 0) for plugin in self.auditor.plugins: base_severity = plugin.severity for issue in plugin.issues: # TODO(buglloc): encapsulate into Issue class? severity = issue.severity if issue.severity else base_severity stats[severity] += 1 return stats def _audit_recursive(self, tree): for directive in tree: self._update_variables(directive) self.auditor.audit(directive) if directive.is_block: if directive.self_context: push_context(directive) self._audit_recursive(directive.children) if directive.self_context: pop_context() def _update_variables(self, directive): # TODO(buglloc): finish him! if not directive.provide_variables: return context = get_context() for var in directive.variables: if var.name == 0: # All regexps must clean indexed variables context.clear_index_vars() context.add_var(var.name, var) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): purge_context()
def main(): parser = _get_cli_parser() args = parser.parse_args() _init_logger(args.debug) path = os.path.expanduser(args.nginx_file) if not os.path.isfile(path): sys.stderr.write('Please specify path to Nginx configuration.\n\n') parser.print_help() sys.exit(1) try: severity = gixy.severity.ALL[args.level] except IndexError: sys.stderr.write( 'Too high level filtering. Maximum level: -{}\n'.format( 'l' * (len(gixy.severity.ALL) - 1))) sys.exit(1) if args.tests: tests = [x.strip() for x in args.tests.split(',')] else: tests = None if args.skips: skips = [x.strip() for x in args.skips.split(',')] else: skips = None config = Config(severity=severity, output_format=args.output_format, output_file=args.output_file, plugins=tests, skips=skips, allow_includes=not args.disable_includes) for plugin_cls in PluginsManager().plugins_classes: name = plugin_cls.__name__ options = copy.deepcopy(plugin_cls.options) for opt_key, opt_val in options.items(): option_name = '{}:{}'.format(name, opt_key) if option_name not in args: continue val = getattr(args, option_name) if val is None: continue if isinstance(opt_val, tuple): val = tuple([x.strip() for x in val.split(',')]) elif isinstance(opt_val, set): val = set([x.strip() for x in val.split(',')]) elif isinstance(opt_val, list): val = [x.strip() for x in val.split(',')] options[opt_key] = val config.set_for(name, options) with Gixy(config=config) as yoda: yoda.audit(path) formatted = formatters()[config.output_format]().format(yoda) if args.output_file: with open(config.output_file, 'w') as f: f.write(formatted) else: print(formatted) if sum(yoda.stats.values()) > 0: # If something found - exit code must be 1, otherwise 0 sys.exit(1) sys.exit(0)