Beispiel #1
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)
Beispiel #2
0
 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}
Beispiel #3
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()
Beispiel #4
0
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
Beispiel #5
0
 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
Beispiel #6
0
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))
Beispiel #7
0
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)
Beispiel #8
0
 def __init__(self, config=None):
     self.root = None
     self.config = config or Config()
     self.auditor = PluginsManager(config=self.config)
Beispiel #9
0
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()
Beispiel #10
0
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)