示例#1
0
def load_filter_ansible(env):
    from ansible.plugins.loader import filter_loader, test_loader

    for path in [
        str(x.resolve())
        for x in Path(
            f"{sys.base_prefix}/lib/python3.8/site-packages/ansible_collections/"
        ).glob("**/plugins/filter")
        if "tests" not in str(x)
    ]:
        filter_loader.add_directory(path)
    for fp in filter_loader.all():
        for filter_name, filter in fp.filters().items():
            path_parts = fp._original_path.split("/")
            if path_parts[-5:-3] == ["site-packages", "ansible"]:
                env.filters[f"ansible.builtin.{filter_name}"] = filter
                env.filters[filter_name] = filter
            else:
                site_packages = path_parts.index("site-packages")
                if path_parts[site_packages + 2 : -3] == [
                    "community",
                    "general",
                ] or path_parts[site_packages + 2 : -3] == ["ansible", "netcommon"]:
                    env.filters[filter_name] = filter
                filter_name = ".".join(
                    path_parts[site_packages + 2 : -3] + [filter_name]
                )
                env.filters[filter_name] = filter

    for fp in test_loader.all():
        env.tests.update(fp.tests())
示例#2
0
def ansible_filters(filter_paths):
    # Load Ansible filters
    for path in filter_paths:
        filter_loader.add_directory(path)
    return {
        k: v
        for filter in filter_loader.all() for k, v in filter.filters().items()
    }
def check(template, out, err):
    env = jinja2.Environment(loader=AbsolutePathLoader())

    for fp in filter_loader.all():
        env.filters.update(fp.filters())

    try:
        env.get_template(template)
        # NOTE(sambetts) Don't output OKs so we keep logs clean
        # out.write("%s: Syntax OK\n" % template)
        return 0
    except jinja2.TemplateNotFound:
        err.write("%s: File not found\n" % template)
        return 2
    except jinja2.exceptions.TemplateSyntaxError as e:
        err.write("%s: Syntax check failed: %s in %s at %d\n" %
                  (template, e.message, e.filename, e.lineno))
        return 1
示例#4
0
def safe_eval(expr, locals=None, include_exceptions=False):
    '''
    This is intended for allowing things like:
    with_items: a_list_variable

    Where Jinja2 would return a string but we do not want to allow it to
    call functions (outside of Jinja2, where the env is constrained).

    Based on:
    http://stackoverflow.com/questions/12523516/using-ast-and-whitelists-to-make-pythons-eval-safe
    '''
    locals = {} if locals is None else locals

    # define certain JSON types
    # eg. JSON booleans are unknown to python eval()
    JSON_TYPES = {
        'false': False,
        'null': None,
        'true': True,
    }

    # this is the whitelist of AST nodes we are going to
    # allow in the evaluation. Any node type other than
    # those listed here will raise an exception in our custom
    # visitor class defined below.
    SAFE_NODES = set(
        (
            ast.Add,
            ast.BinOp,
            # ast.Call,
            ast.Compare,
            ast.Dict,
            ast.Div,
            ast.Expression,
            ast.List,
            ast.Load,
            ast.Mult,
            ast.Num,
            ast.Name,
            ast.Str,
            ast.Sub,
            ast.USub,
            ast.Tuple,
            ast.UnaryOp,
        )
    )

    # AST node types were expanded after 2.6
    if sys.version_info[:2] >= (2, 7):
        SAFE_NODES.update(
            set(
                (ast.Set,)
            )
        )

    # And in Python 3.4 too
    if sys.version_info[:2] >= (3, 4):
        SAFE_NODES.update(
            set(
                (ast.NameConstant,)
            )
        )

    filter_list = []
    for filter_ in filter_loader.all():
        filter_list.extend(filter_.filters().keys())

    test_list = []
    for test in test_loader.all():
        test_list.extend(test.tests().keys())

    CALL_WHITELIST = C.DEFAULT_CALLABLE_WHITELIST + filter_list + test_list

    class CleansingNodeVisitor(ast.NodeVisitor):
        def generic_visit(self, node, inside_call=False):
            if type(node) not in SAFE_NODES:
                raise Exception("invalid expression (%s)" % expr)
            elif isinstance(node, ast.Call):
                inside_call = True
            elif isinstance(node, ast.Name) and inside_call:
                # Disallow calls to builtin functions that we have not vetted
                # as safe.  Other functions are excluded by setting locals in
                # the call to eval() later on
                if hasattr(builtins, node.id) and node.id not in CALL_WHITELIST:
                    raise Exception("invalid function: %s" % node.id)
            # iterate over all child nodes
            for child_node in ast.iter_child_nodes(node):
                self.generic_visit(child_node, inside_call)

    if not isinstance(expr, string_types):
        # already templated to a datastructure, perhaps?
        if include_exceptions:
            return (expr, None)
        return expr

    cnv = CleansingNodeVisitor()
    try:
        parsed_tree = ast.parse(expr, mode='eval')
        cnv.visit(parsed_tree)
        compiled = compile(parsed_tree, expr, 'eval')
        # Note: passing our own globals and locals here constrains what
        # callables (and other identifiers) are recognized.  this is in
        # addition to the filtering of builtins done in CleansingNodeVisitor
        result = eval(compiled, JSON_TYPES, dict(locals))

        if include_exceptions:
            return (result, None)
        else:
            return result
    except SyntaxError as e:
        # special handling for syntax errors, we just return
        # the expression string back as-is to support late evaluation
        if include_exceptions:
            return (expr, None)
        return expr
    except Exception as e:
        if include_exceptions:
            return (expr, e)
        return expr
示例#5
0
def safe_eval(expr, locals=None, include_exceptions=False):
    '''
    This is intended for allowing things like:
    with_items: a_list_variable

    Where Jinja2 would return a string but we do not want to allow it to
    call functions (outside of Jinja2, where the env is constrained).

    Based on:
    http://stackoverflow.com/questions/12523516/using-ast-and-whitelists-to-make-pythons-eval-safe
    '''
    locals = {} if locals is None else locals

    # define certain JSON types
    # eg. JSON booleans are unknown to python eval()
    OUR_GLOBALS = {
        '__builtins__': {},  # avoid global builtins as per eval docs
        'false': False,
        'null': None,
        'true': True,
        # also add back some builtins we do need
        'True': True,
        'False': False,
        'None': None
    }

    # this is the whitelist of AST nodes we are going to
    # allow in the evaluation. Any node type other than
    # those listed here will raise an exception in our custom
    # visitor class defined below.
    SAFE_NODES = set((
        ast.Add,
        ast.BinOp,
        # ast.Call,
        ast.Compare,
        ast.Dict,
        ast.Div,
        ast.Expression,
        ast.List,
        ast.Load,
        ast.Mult,
        ast.Num,
        ast.Name,
        ast.Str,
        ast.Sub,
        ast.USub,
        ast.Tuple,
        ast.UnaryOp,
    ))

    # AST node types were expanded after 2.6
    if sys.version_info[:2] >= (2, 7):
        SAFE_NODES.update(set((ast.Set, )))

    # And in Python 3.4 too
    if sys.version_info[:2] >= (3, 4):
        SAFE_NODES.update(set((ast.NameConstant, )))

    # And in Python 3.6 too, although not encountered until Python 3.8, see https://bugs.python.org/issue32892
    if sys.version_info[:2] >= (3, 6):
        SAFE_NODES.update(set((ast.Constant, )))

    filter_list = []
    for filter_ in filter_loader.all():
        try:
            filter_list.extend(filter_.filters().keys())
        except Exception:
            # This is handled and displayed in JinjaPluginIntercept._load_ansible_plugins
            continue

    test_list = []
    for test in test_loader.all():
        try:
            test_list.extend(test.tests().keys())
        except Exception:
            # This is handled and displayed in JinjaPluginIntercept._load_ansible_plugins
            continue

    CALL_ENABLED = C.CALLABLE_ACCEPT_LIST + filter_list + test_list

    class CleansingNodeVisitor(ast.NodeVisitor):
        def generic_visit(self, node, inside_call=False):
            if type(node) not in SAFE_NODES:
                raise Exception("invalid expression (%s)" % expr)
            elif isinstance(node, ast.Call):
                inside_call = True
            elif isinstance(node, ast.Name) and inside_call:
                # Disallow calls to builtin functions that we have not vetted
                # as safe.  Other functions are excluded by setting locals in
                # the call to eval() later on
                if hasattr(builtins, node.id) and node.id not in CALL_ENABLED:
                    raise Exception("invalid function: %s" % node.id)
            # iterate over all child nodes
            for child_node in ast.iter_child_nodes(node):
                self.generic_visit(child_node, inside_call)

    if not isinstance(expr, string_types):
        # already templated to a datastructure, perhaps?
        if include_exceptions:
            return (expr, None)
        return expr

    cnv = CleansingNodeVisitor()
    try:
        parsed_tree = ast.parse(expr, mode='eval')
        cnv.visit(parsed_tree)
        compiled = compile(parsed_tree, '<expr %s>' % to_native(expr), 'eval')
        # Note: passing our own globals and locals here constrains what
        # callables (and other identifiers) are recognized.  this is in
        # addition to the filtering of builtins done in CleansingNodeVisitor
        result = eval(compiled, OUR_GLOBALS, dict(locals))
        if PY2:
            # On Python 2 u"{'key': 'value'}" is evaluated to {'key': 'value'},
            # ensure it is converted to {u'key': u'value'}.
            result = container_to_text(result)

        if include_exceptions:
            return (result, None)
        else:
            return result
    except SyntaxError as e:
        # special handling for syntax errors, we just return
        # the expression string back as-is to support late evaluation
        if include_exceptions:
            return (expr, None)
        return expr
    except Exception as e:
        if include_exceptions:
            return (expr, e)
        return expr
def ansible_filters(filter_paths):
  # Load Ansible filters  
  for path in filter_paths:
    filter_loader.add_directory(path)
  return { k:v for filter in filter_loader.all() for k,v in filter.filters().items() }
#!/usr/bin/python3

from ansible.plugins.loader import filter_loader
from ansible.release import __version__

output = open('poly-ansible-jinja2-filters.el', 'w')

version_comment = 'Automatically generated for Ansible ' + __version__ + '.'

output.write(";; " + version_comment + "\n\n")
output.write("(defvar poly-ansible-jinja2-filters\n")
output.write("  (list")

for plugin in filter_loader.all():
    for filter_name in plugin.filters():
        output.write('\n   "' + filter_name + '"')

output.write(")\n")
output.write("  \"Additional Jinja2 filters defined by Ansible.\n" +
             version_comment + "\")\n\n")
output.write("(provide 'poly-ansible-jinja2-filters)\n")

output.close()