Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions bandit/plugins/general_bind_all_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

Binding to all network interfaces can potentially open up a service to traffic
on unintended interfaces, that may not be properly documented or secured. This
plugin test looks for a string pattern "0.0.0.0" that may indicate a hardcoded
binding to all network interfaces.
plugin test looks for a string pattern ``"0.0.0.0"`` that may indicate a
hardcoded binding to all network interfaces, and also for ``bind`` calls whose
host argument is the empty string ``""`` -- which the Python socket API treats
as ``INADDR_ANY`` (equivalent to ``"0.0.0.0"``).

:Example:

Expand All @@ -23,27 +25,56 @@
3 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4 s.bind(('0.0.0.0', 31137))
5 s.bind(('192.168.0.1', 8080))
6 s.bind(('', 31137))

.. seealso::

- https://nvd.nist.gov/vuln/detail/CVE-2018-1281
- https://cwe.mitre.org/data/definitions/605.html
- https://docs.python.org/3/library/socket.html#socket-families

.. versionadded:: 0.9.0

.. versionchanged:: 1.7.3
CWE information added

.. versionchanged:: 1.9.5
Detect ``bind(("", port))`` -- empty string resolves to ``INADDR_ANY``.

"""
import bandit
from bandit.core import issue
from bandit.core import test_properties as test


@test.checks("Str")
def _is_empty_string_bind(context):
# The empty string is a Python-socket idiom for INADDR_ANY (it also acts
# as the IPv6 wildcard via getaddrinfo), and so has the same exposure as
# binding to "0.0.0.0". A bare "" literal is far too common to flag
# broadly without an unreasonable false-positive rate, so this check is
# scoped to calls named ``bind`` whose first argument is an address
# tuple/list with "" as its host element.
if context.call_function_name != "bind":
return False
if not context.call_args_count:
return False
address = context.get_call_arg_at_position(0)
if not isinstance(address, (tuple, list)) or not address:
return False
return address[0] == ""


@test.checks("Str", "Call")
@test.test_id("B104")
def hardcoded_bind_all_interfaces(context):
# context.string_val is populated only for Str nodes; for Call nodes the
# plugin dispatches on the call's function name and argument shape.
if context.string_val == "0.0.0.0": # nosec: B104
flagged = True
else:
flagged = _is_empty_string_bind(context)

if flagged:
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.MEDIUM,
Expand Down
6 changes: 6 additions & 0 deletions examples/binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 31137))
s.bind(('192.168.0.1', 8080))
s.bind(('', 31137)) # empty string == INADDR_ANY
s.bind(['', 31137]) # list form, also accepted by socket.bind

# Plain empty-string literals outside of a ``bind`` call must not be flagged.
default_host = ''
host_options = ('localhost', 'example.com')
12 changes: 9 additions & 3 deletions tests/functional/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,16 @@ def check_metrics(self, example_script, expect):
self.assertEqual(expected, m["_totals"][label])

def test_binding(self):
"""Test the bind-to-0.0.0.0 example."""
"""Test the bind-to-0.0.0.0 example.

Expect three issues: one for the ``0.0.0.0`` literal and one each
for the empty-string wildcard host in the tuple and list forms of
``bind``. Plain empty-string literals outside of a ``bind`` call
must not be flagged.
"""
expect = {
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 0},
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 0},
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 3, "HIGH": 0},
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 3, "HIGH": 0},
}
self.check_example("binding.py", expect)

Expand Down