From 40e158a35b4e184b009b2348e5e15f48ca41b03f Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Tue, 28 Jun 2022 16:12:17 -0700 Subject: [PATCH] Avoid calls to ast in plugins If one day we do wish to have the capability to swap the builtin Python ast for another parser, it's best to abstract any direct calls to the ast parser in plugins. This change addresses calls from plugins and other functions to isinstance with ast node types as arguments. Further work is needed for other ast calls in subsequent patches. Related to #894 Signed-off-by: Eric Brown --- bandit/core/blacklisting.py | 6 +- bandit/core/context.py | 22 +++-- bandit/core/utils.py | 11 +++ bandit/plugins/django_sql_injection.py | 17 ++-- bandit/plugins/django_xss.py | 86 +++++++++++--------- bandit/plugins/general_hardcoded_password.py | 44 +++++----- bandit/plugins/injection_shell.py | 23 ++++-- bandit/plugins/injection_sql.py | 12 +-- bandit/plugins/jinja2_templates.py | 5 +- bandit/plugins/try_except_continue.py | 5 +- bandit/plugins/try_except_pass.py | 5 +- 11 files changed, 128 insertions(+), 108 deletions(-) diff --git a/bandit/core/blacklisting.py b/bandit/core/blacklisting.py index 2f84ae023..440ec2ed0 100644 --- a/bandit/core/blacklisting.py +++ b/bandit/core/blacklisting.py @@ -2,10 +2,10 @@ # Copyright 2016 Hewlett-Packard Development Company, L.P. # # SPDX-License-Identifier: Apache-2.0 -import ast import fnmatch from bandit.core import issue +from bandit.core import utils def report_issue(check, name): @@ -34,9 +34,9 @@ def blacklist(context, config): if node_type == "Call": func = context.node.func - if isinstance(func, ast.Name) and func.id == "__import__": + if utils.is_instance(func, "Name") and func.id == "__import__": if len(context.node.args): - if isinstance(context.node.args[0], ast.Str): + if utils.is_instance(context.node.args[0], "Str"): name = context.node.args[0].s else: # TODO(??): import through a variable, need symbol tab diff --git a/bandit/core/context.py b/bandit/core/context.py index 801b36466..a02eb961e 100644 --- a/bandit/core/context.py +++ b/bandit/core/context.py @@ -2,8 +2,6 @@ # Copyright 2014 Hewlett-Packard Development Company, L.P. # # SPDX-License-Identifier: Apache-2.0 -import ast - from bandit.core import utils @@ -178,44 +176,44 @@ def _get_literal_value(self, literal): :param literal: The AST literal to convert :return: The value of the AST literal """ - if isinstance(literal, ast.Num): + if utils.is_instance(literal, "Num"): literal_value = literal.n - elif isinstance(literal, ast.Str): + elif utils.is_instance(literal, "Str"): literal_value = literal.s - elif isinstance(literal, ast.List): + elif utils.is_instance(literal, "List"): return_list = list() for li in literal.elts: return_list.append(self._get_literal_value(li)) literal_value = return_list - elif isinstance(literal, ast.Tuple): + elif utils.is_instance(literal, "Tuple"): return_tuple = tuple() for ti in literal.elts: return_tuple = return_tuple + (self._get_literal_value(ti),) literal_value = return_tuple - elif isinstance(literal, ast.Set): + elif utils.is_instance(literal, "Set"): return_set = set() for si in literal.elts: return_set.add(self._get_literal_value(si)) literal_value = return_set - elif isinstance(literal, ast.Dict): + elif utils.is_instance(literal, "Dict"): literal_value = dict(zip(literal.keys, literal.values)) - elif isinstance(literal, ast.Ellipsis): + elif utils.is_instance(literal, "Ellipsis"): # what do we want to do with this? literal_value = None - elif isinstance(literal, ast.Name): + elif utils.is_instance(literal, "Name"): literal_value = literal.id - elif isinstance(literal, ast.NameConstant): + elif utils.is_instance(literal, "NameConstant"): literal_value = str(literal.value) - elif isinstance(literal, ast.Bytes): + elif utils.is_instance(literal, "Bytes"): literal_value = literal.s else: diff --git a/bandit/core/utils.py b/bandit/core/utils.py index 3ac78f54f..307e7f32f 100644 --- a/bandit/core/utils.py +++ b/bandit/core/utils.py @@ -6,6 +6,7 @@ import logging import os.path import sys +from operator import attrgetter try: import configparser @@ -370,3 +371,13 @@ def check_ast_node(name): pass raise TypeError("Error: %s is not a valid node type in AST" % name) + + +def is_instance(node, type_name): + "Check if the given node is an instance AST type." + if isinstance(type_name, tuple): + f = attrgetter(*type_name) + return isinstance(node, f(ast)) + else: + node_type = getattr(ast, type_name) + return isinstance(node, node_type) diff --git a/bandit/plugins/django_sql_injection.py b/bandit/plugins/django_sql_injection.py index 4d5df1aaa..f87d99735 100644 --- a/bandit/plugins/django_sql_injection.py +++ b/bandit/plugins/django_sql_injection.py @@ -2,17 +2,16 @@ # Copyright (C) 2018 [Victor Torre](https://github.com/ehooo) # # SPDX-License-Identifier: Apache-2.0 -import ast - import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils def keywords2dict(keywords): kwargs = {} for node in keywords: - if isinstance(node, ast.keyword): + if utils.is_instance(node, "keyword"): kwargs[node.arg] = node.value return kwargs @@ -66,23 +65,23 @@ def django_extra_used(context): insecure = False for key in ["where", "tables"]: if key in kwargs: - if isinstance(kwargs[key], ast.List): + if utils.is_instance(kwargs[key], "List"): for val in kwargs[key].elts: - if not isinstance(val, ast.Str): + if not utils.is_instance(val, "Str"): insecure = True break else: insecure = True break if not insecure and "select" in kwargs: - if isinstance(kwargs["select"], ast.Dict): + if utils.is_instance(kwargs["select"], "Dict"): for k in kwargs["select"].keys: - if not isinstance(k, ast.Str): + if not utils.is_instance(k, "Str"): insecure = True break if not insecure: for v in kwargs["select"].values: - if not isinstance(v, ast.Str): + if not utils.is_instance(v, "Str"): insecure = True break else: @@ -130,7 +129,7 @@ def django_rawsql_used(context): if context.is_module_imported_like("django.db.models"): if context.call_function_name == "RawSQL": sql = context.node.args[0] - if not isinstance(sql, ast.Str): + if not utils.is_instance(sql, "Str"): return bandit.Issue( severity=bandit.MEDIUM, confidence=bandit.MEDIUM, diff --git a/bandit/plugins/django_xss.py b/bandit/plugins/django_xss.py index 63a8782b7..a7556b1d6 100644 --- a/bandit/plugins/django_xss.py +++ b/bandit/plugins/django_xss.py @@ -7,6 +7,7 @@ import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils class DeepAssignation: @@ -32,45 +33,45 @@ def is_assigned(self, node): if isinstance(node, self.ignore_nodes): return assigned - if isinstance(node, ast.Expr): + if utils.is_instance(node, "Expr"): assigned = self.is_assigned(node.value) - elif isinstance(node, ast.FunctionDef): + elif utils.is_instance(node, "FunctionDef"): for name in node.args.args: - if isinstance(name, ast.Name): + if utils.is_instance(name, "Name"): if name.id == self.var_name.id: # If is param the assignations are not affected return assigned assigned = self.is_assigned_in(node.body) - elif isinstance(node, ast.With): + elif utils.is_instance(node, "With"): for withitem in node.items: var_id = getattr(withitem.optional_vars, "id", None) if var_id == self.var_name.id: assigned = node else: assigned = self.is_assigned_in(node.body) - elif isinstance(node, ast.Try): + elif utils.is_instance(node, "Try"): assigned = [] assigned.extend(self.is_assigned_in(node.body)) assigned.extend(self.is_assigned_in(node.handlers)) assigned.extend(self.is_assigned_in(node.orelse)) assigned.extend(self.is_assigned_in(node.finalbody)) - elif isinstance(node, ast.ExceptHandler): + elif utils.is_instance(node, "ExceptHandler"): assigned = [] assigned.extend(self.is_assigned_in(node.body)) - elif isinstance(node, (ast.If, ast.For, ast.While)): + elif utils.is_instance(node, ("If", "For", "While")): assigned = [] assigned.extend(self.is_assigned_in(node.body)) assigned.extend(self.is_assigned_in(node.orelse)) - elif isinstance(node, ast.AugAssign): - if isinstance(node.target, ast.Name): + elif utils.is_instance(node, "AugAssign"): + if utils.is_instance(node.target, "Name"): if node.target.id == self.var_name.id: assigned = node.value - elif isinstance(node, ast.Assign) and node.targets: + elif utils.is_instance(node, "Assign") and node.targets: target = node.targets[0] - if isinstance(target, ast.Name): + if utils.is_instance(target, "Name"): if target.id == self.var_name.id: assigned = node.value - elif isinstance(target, ast.Tuple): + elif utils.is_instance(target, "Tuple"): pos = 0 for name in target.elts: if name.id == self.var_name.id: @@ -82,8 +83,8 @@ def is_assigned(self, node): def evaluate_var(xss_var, parent, until, ignore_nodes=None): secure = False - if isinstance(xss_var, ast.Name): - if isinstance(parent, ast.FunctionDef): + if utils.is_instance(xss_var, "Name"): + if utils.is_instance(parent, "FunctionDef"): for name in parent.args.args: if name.arg == xss_var.id: return False # Params are not secure @@ -94,18 +95,18 @@ def evaluate_var(xss_var, parent, until, ignore_nodes=None): break to = analyser.is_assigned(node) if to: - if isinstance(to, ast.Str): + if utils.is_instance(to, "Str"): secure = True - elif isinstance(to, ast.Name): + elif utils.is_instance(to, "Name"): secure = evaluate_var(to, parent, to.lineno, ignore_nodes) - elif isinstance(to, ast.Call): + elif utils.is_instance(to, "Call"): secure = evaluate_call(to, parent, ignore_nodes) elif isinstance(to, (list, tuple)): num_secure = 0 for some_to in to: - if isinstance(some_to, ast.Str): + if utils.is_instance(some_to, "Str"): num_secure += 1 - elif isinstance(some_to, ast.Name): + elif utils.is_instance(some_to, "Name"): if evaluate_var( some_to, parent, node.lineno, ignore_nodes ): @@ -128,8 +129,13 @@ def evaluate_var(xss_var, parent, until, ignore_nodes=None): def evaluate_call(call, parent, ignore_nodes=None): secure = False evaluate = False - if isinstance(call, ast.Call) and isinstance(call.func, ast.Attribute): - if isinstance(call.func.value, ast.Str) and call.func.attr == "format": + if utils.is_instance(call, "Call") and utils.is_instance( + call.func, "Attribute" + ): + if ( + utils.is_instance(call.func.value, "Str") + and call.func.attr == "format" + ): evaluate = True if call.keywords: evaluate = False # TODO(??) get support for this @@ -138,20 +144,20 @@ def evaluate_call(call, parent, ignore_nodes=None): args = list(call.args) num_secure = 0 for arg in args: - if isinstance(arg, ast.Str): + if utils.is_instance(arg, "Str"): num_secure += 1 - elif isinstance(arg, ast.Name): + elif utils.is_instance(arg, "Name"): if evaluate_var(arg, parent, call.lineno, ignore_nodes): num_secure += 1 else: break - elif isinstance(arg, ast.Call): + elif utils.is_instance(arg, "Call"): if evaluate_call(arg, parent, ignore_nodes): num_secure += 1 else: break - elif isinstance(arg, ast.Starred) and isinstance( - arg.value, (ast.List, ast.Tuple) + elif utils.is_instance(arg, "Starred") and utils.is_instance( + arg.value, ("List", "Tuple") ): args.extend(arg.value.elts) num_secure += 1 @@ -163,9 +169,9 @@ def evaluate_call(call, parent, ignore_nodes=None): def transform2call(var): - if isinstance(var, ast.BinOp): - is_mod = isinstance(var.op, ast.Mod) - is_left_str = isinstance(var.left, ast.Str) + if utils.is_instance(var, "BinOp"): + is_mod = utils.is_instance(var.op, "Mod") + is_left_str = utils.is_instance(var.left, "Str") if is_mod and is_left_str: new_call = ast.Call() new_call.args = [] @@ -175,7 +181,7 @@ def transform2call(var): new_call.func = ast.Attribute() new_call.func.value = var.left new_call.func.attr = "format" - if isinstance(var.right, ast.Tuple): + if utils.is_instance(var.right, "Tuple"): new_call.args = var.right.elts else: new_call.args = [var.right] @@ -188,14 +194,14 @@ def check_risk(node): secure = False - if isinstance(xss_var, ast.Name): + if utils.is_instance(xss_var, "Name"): # Check if the var are secure parent = node._bandit_parent - while not isinstance(parent, (ast.Module, ast.FunctionDef)): + while not utils.is_instance(parent, ("Module", "FunctionDef")): parent = parent._bandit_parent is_param = False - if isinstance(parent, ast.FunctionDef): + if utils.is_instance(parent, "FunctionDef"): for name in parent.args.args: if name.arg == xss_var.id: is_param = True @@ -203,17 +209,17 @@ def check_risk(node): if not is_param: secure = evaluate_var(xss_var, parent, node.lineno) - elif isinstance(xss_var, ast.Call): + elif utils.is_instance(xss_var, "Call"): parent = node._bandit_parent - while not isinstance(parent, (ast.Module, ast.FunctionDef)): + while not utils.is_instance(parent, ("Module", "FunctionDef")): parent = parent._bandit_parent secure = evaluate_call(xss_var, parent) - elif isinstance(xss_var, ast.BinOp): - is_mod = isinstance(xss_var.op, ast.Mod) - is_left_str = isinstance(xss_var.left, ast.Str) + elif utils.is_instance(xss_var, "BinOp"): + is_mod = utils.is_instance(xss_var.op, "Mod") + is_left_str = utils.is_instance(xss_var.left, "Str") if is_mod and is_left_str: parent = node._bandit_parent - while not isinstance(parent, (ast.Module, ast.FunctionDef)): + while not utils.is_instance(parent, ("Module", "FunctionDef")): parent = parent._bandit_parent new_call = transform2call(xss_var) secure = evaluate_call(new_call, parent) @@ -270,5 +276,5 @@ def django_mark_safe(context): ] if context.call_function_name in affected_functions: xss = context.node.args[0] - if not isinstance(xss, ast.Str): + if not utils.is_instance(xss, "Str"): return check_risk(context.node) diff --git a/bandit/plugins/general_hardcoded_password.py b/bandit/plugins/general_hardcoded_password.py index 9a162b485..508099753 100644 --- a/bandit/plugins/general_hardcoded_password.py +++ b/bandit/plugins/general_hardcoded_password.py @@ -2,12 +2,12 @@ # Copyright 2014 Hewlett-Packard Development Company, L.P. # # SPDX-License-Identifier: Apache-2.0 -import ast import re import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils RE_WORDS = "(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?)" @@ -80,48 +80,50 @@ def hardcoded_password_string(context): """ node = context.node - if isinstance(node._bandit_parent, ast.Assign): + if utils.is_instance(node._bandit_parent, "Assign"): # looks for "candidate='some_string'" for targ in node._bandit_parent.targets: - if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id): + if utils.is_instance(targ, "Name") and RE_CANDIDATES.search( + targ.id + ): return _report(node.s) - elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search( + elif utils.is_instance(targ, "Attribute") and RE_CANDIDATES.search( targ.attr ): return _report(node.s) - elif isinstance( - node._bandit_parent, ast.Subscript + elif utils.is_instance( + node._bandit_parent, "Subscript" ) and RE_CANDIDATES.search(node.s): # Py39+: looks for "dict[candidate]='some_string'" # subscript -> index -> string assign = node._bandit_parent._bandit_parent - if isinstance(assign, ast.Assign) and isinstance( - assign.value, ast.Str + if utils.is_instance(assign, "Assign") and utils.is_instance( + assign.value, "Str" ): return _report(assign.value.s) - elif isinstance(node._bandit_parent, ast.Index) and RE_CANDIDATES.search( - node.s - ): + elif utils.is_instance( + node._bandit_parent, "Index" + ) and RE_CANDIDATES.search(node.s): # looks for "dict[candidate]='some_string'" # assign -> subscript -> index -> string assign = node._bandit_parent._bandit_parent._bandit_parent - if isinstance(assign, ast.Assign) and isinstance( - assign.value, ast.Str + if utils.is_instance(assign, "Assign") and utils.is_instance( + assign.value, "Str" ): return _report(assign.value.s) - elif isinstance(node._bandit_parent, ast.Compare): + elif utils.is_instance(node._bandit_parent, "Compare"): # looks for "candidate == 'some_string'" comp = node._bandit_parent - if isinstance(comp.left, ast.Name): + if utils.is_instance(comp.left, "Name"): if RE_CANDIDATES.search(comp.left.id): - if isinstance(comp.comparators[0], ast.Str): + if utils.is_instance(comp.comparators[0], "Str"): return _report(comp.comparators[0].s) - elif isinstance(comp.left, ast.Attribute): + elif utils.is_instance(comp.left, "Attribute"): if RE_CANDIDATES.search(comp.left.attr): - if isinstance(comp.comparators[0], ast.Str): + if utils.is_instance(comp.comparators[0], "Str"): return _report(comp.comparators[0].s) @@ -177,7 +179,7 @@ def hardcoded_password_funcarg(context): """ # looks for "function(candidate='some_string')" for kw in context.node.keywords: - if isinstance(kw.value, ast.Str) and RE_CANDIDATES.search(kw.arg): + if utils.is_instance(kw.value, "Str") and RE_CANDIDATES.search(kw.arg): return _report(kw.value.s) @@ -242,6 +244,6 @@ def hardcoded_password_default(context): # go through all (param, value)s and look for candidates for key, val in zip(context.node.args.args, defs): - if isinstance(key, (ast.Name, ast.arg)): - if isinstance(val, ast.Str) and RE_CANDIDATES.search(key.arg): + if utils.is_instance(key, ("Name", "arg")): + if utils.is_instance(val, "Str") and RE_CANDIDATES.search(key.arg): return _report(val.s) diff --git a/bandit/plugins/injection_shell.py b/bandit/plugins/injection_shell.py index 19c31d209..a741bb73a 100644 --- a/bandit/plugins/injection_shell.py +++ b/bandit/plugins/injection_shell.py @@ -2,12 +2,12 @@ # Copyright 2014 Hewlett-Packard Development Company, L.P. # # SPDX-License-Identifier: Apache-2.0 -import ast import re import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils # yuck, regex: starts with a windows drive letter (eg C:) @@ -16,7 +16,7 @@ def _evaluate_shell_call(context): - no_formatting = isinstance(context.node.args[0], ast.Str) + no_formatting = utils.is_instance(context.node.args[0], "Str") if no_formatting: return bandit.LOW @@ -82,15 +82,18 @@ def has_shell(context): for key in keywords: if key.arg == "shell": val = key.value - if isinstance(val, ast.Num): + if utils.is_instance(val, "Num"): result = bool(val.n) - elif isinstance(val, ast.List): + elif utils.is_instance(val, "List"): result = bool(val.elts) - elif isinstance(val, ast.Dict): + elif utils.is_instance(val, "Dict"): result = bool(val.keys) - elif isinstance(val, ast.Name) and val.id in ["False", "None"]: + elif utils.is_instance(val, "Name") and val.id in [ + "False", + "None", + ]: result = False - elif isinstance(val, ast.NameConstant): + elif utils.is_instance(val, "NameConstant"): result = val.value else: result = True @@ -681,11 +684,13 @@ def start_process_with_partial_path(context, config): node = context.node.args[0] # some calls take an arg list, check the first part - if isinstance(node, ast.List): + if utils.is_instance(node, "List"): node = node.elts[0] # make sure the param is a string literal and not a var name - if isinstance(node, ast.Str) and not full_path_match.match(node.s): + if utils.is_instance(node, "Str") and not full_path_match.match( + node.s + ): return bandit.Issue( severity=bandit.LOW, confidence=bandit.HIGH, diff --git a/bandit/plugins/injection_sql.py b/bandit/plugins/injection_sql.py index c69750ca1..cee419f2a 100644 --- a/bandit/plugins/injection_sql.py +++ b/bandit/plugins/injection_sql.py @@ -53,7 +53,6 @@ CWE information added """ # noqa: E501 -import ast import re import bandit @@ -61,6 +60,7 @@ from bandit.core import test_properties as test from bandit.core import utils + SIMPLE_SQL_RE = re.compile( r"(select\s.*from\s|" r"delete\s+from\s|" @@ -78,24 +78,24 @@ def _evaluate_ast(node): wrapper = None statement = "" - if isinstance(node._bandit_parent, ast.BinOp): + if utils.is_instance(node._bandit_parent, "BinOp"): out = utils.concat_string(node, node._bandit_parent) wrapper = out[0]._bandit_parent statement = out[1] elif ( - isinstance(node._bandit_parent, ast.Attribute) + utils.is_instance(node._bandit_parent, "Attribute") and node._bandit_parent.attr == "format" ): statement = node.s # Hierarchy for "".format() is Wrapper -> Call -> Attribute -> Str wrapper = node._bandit_parent._bandit_parent._bandit_parent - elif hasattr(ast, "JoinedStr") and isinstance( - node._bandit_parent, ast.JoinedStr + elif utils.check_ast_node("JoinedStr") and utils.is_instance( + node._bandit_parent, "JoinedStr" ): statement = node.s wrapper = node._bandit_parent._bandit_parent - if isinstance(wrapper, ast.Call): # wrapped in "execute" call? + if utils.is_instance(wrapper, "Call"): # wrapped in "execute" call? names = ["execute", "executemany"] name = utils.get_called_name(wrapper) return (name in names, statement) diff --git a/bandit/plugins/jinja2_templates.py b/bandit/plugins/jinja2_templates.py index f0b23e03b..2e05d14a6 100644 --- a/bandit/plugins/jinja2_templates.py +++ b/bandit/plugins/jinja2_templates.py @@ -68,6 +68,7 @@ import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils @test.checks("Call") @@ -79,7 +80,7 @@ def jinja2_autoescape_false(context): func = qualname_list[-1] if "jinja2" in qualname_list and func == "Environment": for node in ast.walk(context.node): - if isinstance(node, ast.keyword): + if utils.is_instance(node, "keyword"): # definite autoescape = False if getattr(node, "arg", None) == "autoescape" and ( getattr(node.value, "id", None) == "False" @@ -105,7 +106,7 @@ def jinja2_autoescape_false(context): return # Check if select_autoescape function is used. elif ( - isinstance(value, ast.Call) + utils.is_instance(value, "Call") and getattr(value.func, "id", None) == "select_autoescape" ): diff --git a/bandit/plugins/try_except_continue.py b/bandit/plugins/try_except_continue.py index c2e3ad493..6d0c5682b 100644 --- a/bandit/plugins/try_except_continue.py +++ b/bandit/plugins/try_except_continue.py @@ -74,11 +74,10 @@ class (or no type). To accommodate this, the test may be configured to ignore CWE information added """ -import ast - import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils def gen_config(name): @@ -99,7 +98,7 @@ def try_except_continue(context, config): ): return - if isinstance(node.body[0], ast.Continue): + if utils.is_instance(node.body[0], "Continue"): return bandit.Issue( severity=bandit.LOW, confidence=bandit.HIGH, diff --git a/bandit/plugins/try_except_pass.py b/bandit/plugins/try_except_pass.py index eda0ef800..091557b0c 100644 --- a/bandit/plugins/try_except_pass.py +++ b/bandit/plugins/try_except_pass.py @@ -72,11 +72,10 @@ class (or no type). To accommodate this, the test may be configured to ignore CWE information added """ -import ast - import bandit from bandit.core import issue from bandit.core import test_properties as test +from bandit.core import utils def gen_config(name): @@ -97,7 +96,7 @@ def try_except_pass(context, config): ): return - if isinstance(node.body[0], ast.Pass): + if utils.is_instance(node.body[0], "Pass"): return bandit.Issue( severity=bandit.LOW, confidence=bandit.HIGH,