What is the way to ignore/skip some issues from python bandit security issues report?
Question:
I’ve got a bunch of django_mark_safe
errors
>> Issue: [B703:django_mark_safe] Potential XSS on mark_safe function.
Severity: Medium Confidence: High
Location: ...
More Info: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html
54 return mark_safe(f'<a href="{url}" target="_blank">{title}</a>')
>> Issue: [B308:blacklist] Use of mark_safe() may expose cross-site scripting vulnerabilities and should be reviewed.
Severity: Medium Confidence: High
Location: ...
More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b308-mark-safe
54 return mark_safe(f'<a href="{url}" target="_blank">{title}</a>')
And I’m curious if there is a way to skip or ignore such lines? I understand that using mark_safe
could be dangerous, but what if I want to take the risk? For example this method is the only way to display custom link in Django admin, so I don’t know any other option how to do it without mark_safe
Answers:
I’ve got an answer here:
Two ways:
- You can skip the B703 and B308 using the –skip argument to the
command line.
- Or you can affix a comment
# nosec
on the line to skip.
https://bandit.readthedocs.io/en/latest/config.html#exclusions
Heads up for annotating multilines with # nosec
:
given:
li_without_nosec = [
"select * from %s where 1 = 1 "
% "foo"
]
li_nosec_at_start_works = [ # nosec - ✅ and you can put a comment
"select * from %s where 1 = 1 "
% "foo"
]
# nosec - there's an enhancement request to marker above line
li_nosec_on_top_doesntwork = [
"select * from %s where 1 = 1 "
% "foo"
]
li_nosec_at_end_doesntwork = [
"select * from %s where 1 = 1 "
% "foo"
] # nosec
output:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:3
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
2 li_without_nosec = [
3 "select * from %s where 1 = 1 "
4 % "foo"
5 ]
--------------------------------------------------
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:15
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
14 li_nosec_on_top_doesntwork = [
15 "select * from %s where 1 = 1 "
16 % "foo"
17 ]
--------------------------------------------------
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:21
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
20 li_nosec_at_end_doesntwork = [
21 "select * from %s where 1 = 1 "
22 % "foo"
23 ] # nosec
Black
Here’s hoping that black won’t get involved and restructure the lines, moving the # nosec
around.
so much for hope… black does move things around, just like it does with pylint directives, whenever the line length becomes too long. At which point # nosec
ends up at the end.
You can either proactively break up the line and position # nosec
at the first one. Or you can just wait out black and adjust if needed.
Just to complete the topic – in my case I had to rid of B322: input
rule, and didn’t wanted to write # nosec
each time I found this problem in the code, or to always execute Bandit with a --skip
flag.
So if you want to omit a certain rule for whole solution, you can create a .bandit
file in the root of your project. Then you can write which rules should be skipped every time, for example:
[bandit]
skips: B322
And then Bandit will skip this check by default without need to give additional comments in the code.
You can config Bandit with .bandit
INI file (only if it is invoked with -r
option):
[bandit]
tests = B101,B102,B301
Or with pyproject.toml
file:
[tool.bandit]
tests = ["B201", "B301"]
skips = ["B101", "B601"]
Or with yaml file:
skips: ['B101', 'B601']
assert_used:
skips: ["*/test_*.py", "*/test_*.py"]
If you want to apply the rule in a local piece of your code you can do something like the following:
# this is my very basic python script
def foo():
do_something_unsecure() # nosec B703, B308
In that way you will skip those validations just in that line. In real work it can be probably the most appropriated way to skip some rules.
I’ve got a bunch of django_mark_safe
errors
>> Issue: [B703:django_mark_safe] Potential XSS on mark_safe function.
Severity: Medium Confidence: High
Location: ...
More Info: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html
54 return mark_safe(f'<a href="{url}" target="_blank">{title}</a>')
>> Issue: [B308:blacklist] Use of mark_safe() may expose cross-site scripting vulnerabilities and should be reviewed.
Severity: Medium Confidence: High
Location: ...
More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b308-mark-safe
54 return mark_safe(f'<a href="{url}" target="_blank">{title}</a>')
And I’m curious if there is a way to skip or ignore such lines? I understand that using mark_safe
could be dangerous, but what if I want to take the risk? For example this method is the only way to display custom link in Django admin, so I don’t know any other option how to do it without mark_safe
I’ve got an answer here:
Two ways:
- You can skip the B703 and B308 using the –skip argument to the
command line.- Or you can affix a comment
# nosec
on the line to skip.https://bandit.readthedocs.io/en/latest/config.html#exclusions
Heads up for annotating multilines with # nosec
:
given:
li_without_nosec = [
"select * from %s where 1 = 1 "
% "foo"
]
li_nosec_at_start_works = [ # nosec - ✅ and you can put a comment
"select * from %s where 1 = 1 "
% "foo"
]
# nosec - there's an enhancement request to marker above line
li_nosec_on_top_doesntwork = [
"select * from %s where 1 = 1 "
% "foo"
]
li_nosec_at_end_doesntwork = [
"select * from %s where 1 = 1 "
% "foo"
] # nosec
output:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:3
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
2 li_without_nosec = [
3 "select * from %s where 1 = 1 "
4 % "foo"
5 ]
--------------------------------------------------
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:15
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
14 li_nosec_on_top_doesntwork = [
15 "select * from %s where 1 = 1 "
16 % "foo"
17 ]
--------------------------------------------------
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
Severity: Medium Confidence: Low
Location: test.py:21
More Info: https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html
20 li_nosec_at_end_doesntwork = [
21 "select * from %s where 1 = 1 "
22 % "foo"
23 ] # nosec
Black
Here’s hoping that black won’t get involved and restructure the lines, moving the # nosec
around.
so much for hope… black does move things around, just like it does with pylint directives, whenever the line length becomes too long. At which point # nosec
ends up at the end.
You can either proactively break up the line and position # nosec
at the first one. Or you can just wait out black and adjust if needed.
Just to complete the topic – in my case I had to rid of B322: input
rule, and didn’t wanted to write # nosec
each time I found this problem in the code, or to always execute Bandit with a --skip
flag.
So if you want to omit a certain rule for whole solution, you can create a .bandit
file in the root of your project. Then you can write which rules should be skipped every time, for example:
[bandit]
skips: B322
And then Bandit will skip this check by default without need to give additional comments in the code.
You can config Bandit with .bandit
INI file (only if it is invoked with -r
option):
[bandit]
tests = B101,B102,B301
Or with pyproject.toml
file:
[tool.bandit]
tests = ["B201", "B301"]
skips = ["B101", "B601"]
Or with yaml file:
skips: ['B101', 'B601']
assert_used:
skips: ["*/test_*.py", "*/test_*.py"]
If you want to apply the rule in a local piece of your code you can do something like the following:
# this is my very basic python script
def foo():
do_something_unsecure() # nosec B703, B308
In that way you will skip those validations just in that line. In real work it can be probably the most appropriated way to skip some rules.