Skip to content

Commit

Permalink
Revert "Start testing with 3.14 alphas" (#1217)
Browse files Browse the repository at this point in the history
Revert "Start testing with 3.14 alphas (#1189)"

This reverts commit 13d3406.
ericwb authored Jan 12, 2025

Verified

This commit was signed with the committer’s verified signature.
BeiyanYunyi 北雁云依
1 parent e58379c commit c2c336d
Showing 12 changed files with 76 additions and 83 deletions.
1 change: 0 additions & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -52,7 +52,6 @@ jobs:
["3.11", "311"],
["3.12", "312"],
["3.13", "313"],
["3.14.0-alpha - 3.14", "314"],
]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
4 changes: 2 additions & 2 deletions bandit/core/blacklisting.py
Original file line number Diff line number Diff line change
@@ -35,8 +35,8 @@ def blacklist(context, config):
func = context.node.func
if isinstance(func, ast.Name) and func.id == "__import__":
if len(context.node.args):
if isinstance(context.node.args[0], ast.Constant):
name = context.node.args[0].value
if isinstance(context.node.args[0], ast.Str):
name = context.node.args[0].s
else:
# TODO(??): import through a variable, need symbol tab
name = "UNKNOWN"
22 changes: 15 additions & 7 deletions bandit/core/context.py
Original file line number Diff line number Diff line change
@@ -178,13 +178,11 @@ def _get_literal_value(self, literal):
:param literal: The AST literal to convert
:return: The value of the AST literal
"""
if isinstance(literal, ast.Constant):
if isinstance(literal.value, bool):
literal_value = str(literal.value)
elif literal.value is None:
literal_value = str(literal.value)
else:
literal_value = literal.value
if isinstance(literal, ast.Num):
literal_value = literal.n

elif isinstance(literal, ast.Str):
literal_value = literal.s

elif isinstance(literal, ast.List):
return_list = list()
@@ -207,9 +205,19 @@ def _get_literal_value(self, literal):
elif isinstance(literal, ast.Dict):
literal_value = dict(zip(literal.keys, literal.values))

elif isinstance(literal, ast.Ellipsis):
# what do we want to do with this?
literal_value = None

elif isinstance(literal, ast.Name):
literal_value = literal.id

elif isinstance(literal, ast.NameConstant):
literal_value = str(literal.value)

elif isinstance(literal, ast.Bytes):
literal_value = literal.s

else:
literal_value = None

4 changes: 2 additions & 2 deletions bandit/core/node_visitor.py
Original file line number Diff line number Diff line change
@@ -168,7 +168,7 @@ def visit_Str(self, node):
:param node: The node that is being inspected
:return: -
"""
self.context["str"] = node.value
self.context["str"] = node.s
if not isinstance(node._bandit_parent, ast.Expr): # docstring
self.context["linerange"] = b_utils.linerange(node._bandit_parent)
self.update_scores(self.tester.run_tests(self.context, "Str"))
@@ -181,7 +181,7 @@ def visit_Bytes(self, node):
:param node: The node that is being inspected
:return: -
"""
self.context["bytes"] = node.value
self.context["bytes"] = node.s
if not isinstance(node._bandit_parent, ast.Expr): # docstring
self.context["linerange"] = b_utils.linerange(node._bandit_parent)
self.update_scores(self.tester.run_tests(self.context, "Bytes"))
22 changes: 4 additions & 18 deletions bandit/core/utils.py
Original file line number Diff line number Diff line change
@@ -273,12 +273,12 @@ def linerange(node):
def concat_string(node, stop=None):
"""Builds a string from a ast.BinOp chain.
This will build a string from a series of ast.Constant nodes wrapped in
This will build a string from a series of ast.Str nodes wrapped in
ast.BinOp nodes. Something like "a" + "b" + "c" or "a %s" % val etc.
The provided node can be any participant in the BinOp chain.
:param node: (ast.Constant or ast.BinOp) The node to process
:param stop: (ast.Constant or ast.BinOp) Optional base node to stop at
:param node: (ast.Str or ast.BinOp) The node to process
:param stop: (ast.Str or ast.BinOp) Optional base node to stop at
:returns: (Tuple) the root node of the expression, the string value
"""

@@ -300,10 +300,7 @@ def _get(node, bits, stop=None):
node = node._bandit_parent
if isinstance(node, ast.BinOp):
_get(node, bits, stop)
return (
node,
" ".join([x.value for x in bits if isinstance(x, ast.Constant)]),
)
return (node, " ".join([x.s for x in bits if isinstance(x, ast.Str)]))


def get_called_name(node):
@@ -364,17 +361,6 @@ def parse_ini_file(f_loc):
def check_ast_node(name):
"Check if the given name is that of a valid AST node."
try:
# These ast Node types don't exist in Python 3.14, but plugins may
# still check on them.
if sys.version_info >= (3, 14) and name in (
"Num",
"Str",
"Ellipsis",
"NameConstant",
"Bytes",
):
return name

node = getattr(ast, name)
if issubclass(node, ast.AST):
return name
8 changes: 4 additions & 4 deletions bandit/plugins/django_sql_injection.py
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ def django_extra_used(context):
if key in kwargs:
if isinstance(kwargs[key], ast.List):
for val in kwargs[key].elts:
if not isinstance(val, ast.Constant):
if not isinstance(val, ast.Str):
insecure = True
break
else:
@@ -77,12 +77,12 @@ def django_extra_used(context):
if not insecure and "select" in kwargs:
if isinstance(kwargs["select"], ast.Dict):
for k in kwargs["select"].keys:
if not isinstance(k, ast.Constant):
if not isinstance(k, ast.Str):
insecure = True
break
if not insecure:
for v in kwargs["select"].values:
if not isinstance(v, ast.Constant):
if not isinstance(v, ast.Str):
insecure = True
break
else:
@@ -135,7 +135,7 @@ def django_rawsql_used(context):
kwargs = keywords2dict(context.node.keywords)
sql = kwargs["sql"]

if not isinstance(sql, ast.Constant):
if not isinstance(sql, ast.Str):
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.MEDIUM,
17 changes: 7 additions & 10 deletions bandit/plugins/django_xss.py
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ def evaluate_var(xss_var, parent, until, ignore_nodes=None):
break
to = analyser.is_assigned(node)
if to:
if isinstance(to, ast.Constant):
if isinstance(to, ast.Str):
secure = True
elif isinstance(to, ast.Name):
secure = evaluate_var(to, parent, to.lineno, ignore_nodes)
@@ -105,7 +105,7 @@ def evaluate_var(xss_var, parent, until, ignore_nodes=None):
elif isinstance(to, (list, tuple)):
num_secure = 0
for some_to in to:
if isinstance(some_to, ast.Constant):
if isinstance(some_to, ast.Str):
num_secure += 1
elif isinstance(some_to, ast.Name):
if evaluate_var(
@@ -131,10 +131,7 @@ 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.Constant)
and call.func.attr == "format"
):
if isinstance(call.func.value, ast.Str) and call.func.attr == "format":
evaluate = True
if call.keywords:
evaluate = False # TODO(??) get support for this
@@ -143,7 +140,7 @@ def evaluate_call(call, parent, ignore_nodes=None):
args = list(call.args)
num_secure = 0
for arg in args:
if isinstance(arg, ast.Constant):
if isinstance(arg, ast.Str):
num_secure += 1
elif isinstance(arg, ast.Name):
if evaluate_var(arg, parent, call.lineno, ignore_nodes):
@@ -170,7 +167,7 @@ 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.Constant)
is_left_str = isinstance(var.left, ast.Str)
if is_mod and is_left_str:
new_call = ast.Call()
new_call.args = []
@@ -215,7 +212,7 @@ def check_risk(node):
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.Constant)
is_left_str = isinstance(xss_var.left, ast.Str)
if is_mod and is_left_str:
parent = node._bandit_parent
while not isinstance(parent, (ast.Module, ast.FunctionDef)):
@@ -275,5 +272,5 @@ def django_mark_safe(context):
]
if context.call_function_name in affected_functions:
xss = context.node.args[0]
if not isinstance(xss, ast.Constant):
if not isinstance(xss, ast.Str):
return check_risk(context.node)
32 changes: 16 additions & 16 deletions bandit/plugins/general_hardcoded_password.py
Original file line number Diff line number Diff line change
@@ -83,45 +83,45 @@ def hardcoded_password_string(context):
# looks for "candidate='some_string'"
for targ in node._bandit_parent.targets:
if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id):
return _report(node.value)
return _report(node.s)
elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search(
targ.attr
):
return _report(node.value)
return _report(node.s)

elif isinstance(
node._bandit_parent, ast.Subscript
) and RE_CANDIDATES.search(node.value):
) 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.Constant
assign.value, ast.Str
):
return _report(assign.value.value)
return _report(assign.value.s)

elif isinstance(node._bandit_parent, ast.Index) and RE_CANDIDATES.search(
node.value
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.Constant
assign.value, ast.Str
):
return _report(assign.value.value)
return _report(assign.value.s)

elif isinstance(node._bandit_parent, ast.Compare):
# looks for "candidate == 'some_string'"
comp = node._bandit_parent
if isinstance(comp.left, ast.Name):
if RE_CANDIDATES.search(comp.left.id):
if isinstance(comp.comparators[0], ast.Constant):
return _report(comp.comparators[0].value)
if isinstance(comp.comparators[0], ast.Str):
return _report(comp.comparators[0].s)
elif isinstance(comp.left, ast.Attribute):
if RE_CANDIDATES.search(comp.left.attr):
if isinstance(comp.comparators[0], ast.Constant):
return _report(comp.comparators[0].value)
if isinstance(comp.comparators[0], ast.Str):
return _report(comp.comparators[0].s)


@test.checks("Call")
@@ -176,8 +176,8 @@ def hardcoded_password_funcarg(context):
"""
# looks for "function(candidate='some_string')"
for kw in context.node.keywords:
if isinstance(kw.value, ast.Constant) and RE_CANDIDATES.search(kw.arg):
return _report(kw.value.value)
if isinstance(kw.value, ast.Str) and RE_CANDIDATES.search(kw.arg):
return _report(kw.value.s)


@test.checks("FunctionDef")
@@ -242,5 +242,5 @@ 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.Constant) and RE_CANDIDATES.search(key.arg):
return _report(val.value)
if isinstance(val, ast.Str) and RE_CANDIDATES.search(key.arg):
return _report(val.s)
12 changes: 6 additions & 6 deletions bandit/plugins/injection_shell.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@


def _evaluate_shell_call(context):
no_formatting = isinstance(context.node.args[0], ast.Constant)
no_formatting = isinstance(context.node.args[0], ast.Str)

if no_formatting:
return bandit.LOW
@@ -83,14 +83,16 @@ def has_shell(context):
for key in keywords:
if key.arg == "shell":
val = key.value
if isinstance(val, ast.Constant):
result = bool(val.value)
if isinstance(val, ast.Num):
result = bool(val.n)
elif isinstance(val, ast.List):
result = bool(val.elts)
elif isinstance(val, ast.Dict):
result = bool(val.keys)
elif isinstance(val, ast.Name) and val.id in ["False", "None"]:
result = False
elif isinstance(val, ast.NameConstant):
result = val.value
else:
result = True
return result
@@ -685,9 +687,7 @@ def start_process_with_partial_path(context, config):
node = node.elts[0]

# make sure the param is a string literal and not a var name
if isinstance(node, ast.Constant) and not full_path_match.match(
node.value
):
if isinstance(node, ast.Str) and not full_path_match.match(node.s):
return bandit.Issue(
severity=bandit.LOW,
confidence=bandit.HIGH,
6 changes: 3 additions & 3 deletions bandit/plugins/injection_sql.py
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ def _evaluate_ast(node):
elif isinstance(
node._bandit_parent, ast.Attribute
) and node._bandit_parent.attr in ("format", "replace"):
statement = node.value
statement = node.s
# Hierarchy for "".format() is Wrapper -> Call -> Attribute -> Str
wrapper = node._bandit_parent._bandit_parent._bandit_parent
if node._bandit_parent.attr == "replace":
@@ -107,14 +107,14 @@ def _evaluate_ast(node):
substrings = [
child
for child in node._bandit_parent.values
if isinstance(child, ast.Constant)
if isinstance(child, ast.Str)
]
# JoinedStr consists of list of Constant and FormattedValue
# instances. Let's perform one test for the whole string
# and abandon all parts except the first one to raise one
# failed test instead of many for the same SQL statement.
if substrings and node == substrings[0]:
statement = "".join([str(child.value) for child in substrings])
statement = "".join([str(child.s) for child in substrings])
wrapper = node._bandit_parent._bandit_parent

if isinstance(wrapper, ast.Call): # wrapped in "execute" call?
2 changes: 1 addition & 1 deletion bandit/plugins/tarfile_unsafe_members.py
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ def is_filter_data(context):
for keyword in context.node.keywords:
if keyword.arg == "filter":
arg = keyword.value
return isinstance(arg, ast.Constant) and arg.value == "data"
return isinstance(arg, ast.Str) and arg.s == "data"


@test.test_id("B202")
29 changes: 16 additions & 13 deletions tests/unit/core/test_context.py
Original file line number Diff line number Diff line change
@@ -132,36 +132,39 @@ def test_function_def_defaults_qual(self, get_qual_attr):
def test__get_literal_value(self):
new_context = context.Context()

value = ast.Constant(42)
expected = value.value
value = ast.Num(42)
expected = value.n
self.assertEqual(expected, new_context._get_literal_value(value))

value = ast.Constant("spam")
expected = value.value
value = ast.Str("spam")
expected = value.s
self.assertEqual(expected, new_context._get_literal_value(value))

value = ast.List([ast.Constant("spam"), ast.Constant(42)], ast.Load())
expected = [ast.Constant("spam").value, ast.Constant(42).value]
value = ast.List([ast.Str("spam"), ast.Num(42)], ast.Load())
expected = [ast.Str("spam").s, ast.Num(42).n]
self.assertListEqual(expected, new_context._get_literal_value(value))

value = ast.Tuple([ast.Constant("spam"), ast.Constant(42)], ast.Load())
expected = (ast.Constant("spam").value, ast.Constant(42).value)
value = ast.Tuple([ast.Str("spam"), ast.Num(42)], ast.Load())
expected = (ast.Str("spam").s, ast.Num(42).n)
self.assertTupleEqual(expected, new_context._get_literal_value(value))

value = ast.Set([ast.Constant("spam"), ast.Constant(42)])
expected = {ast.Constant("spam").value, ast.Constant(42).value}
value = ast.Set([ast.Str("spam"), ast.Num(42)])
expected = {ast.Str("spam").s, ast.Num(42).n}
self.assertSetEqual(expected, new_context._get_literal_value(value))

value = ast.Dict(["spam", "eggs"], [42, "foo"])
expected = dict(spam=42, eggs="foo")
self.assertDictEqual(expected, new_context._get_literal_value(value))

value = ast.Ellipsis()
self.assertIsNone(new_context._get_literal_value(value))

value = ast.Name("spam", ast.Load())
expected = value.id
self.assertEqual(expected, new_context._get_literal_value(value))

value = ast.Constant(b"spam")
expected = value.value
value = ast.Bytes(b"spam")
expected = value.s
self.assertEqual(expected, new_context._get_literal_value(value))

self.assertIsNone(new_context._get_literal_value(None))
@@ -204,7 +207,7 @@ def test_get_lineno_for_call_arg(self, node):
def test_get_call_arg_at_position(self):
expected_arg = "spam"
ref_call = mock.Mock()
ref_call.args = [ast.Constant(expected_arg)]
ref_call.args = [ast.Str(expected_arg)]
ref_context = dict(call=ref_call)
new_context = context.Context(context_object=ref_context)
self.assertEqual(expected_arg, new_context.get_call_arg_at_position(0))

0 comments on commit c2c336d

Please sign in to comment.