Add unaliased mod name to import_aliases; Fix tests

In order to identify what module an imported symbol came from
we need to be able to look it up in the list of imported symbols
for the module. These symbols could be aliased but don't
have to be. In both case we want to be able to look it up.
We handled when an symbol was aliased on import, but not
when it wasn't. This meant we could not get a true fully
qualified name for that symbol at its point of use.

For example:
  import a from b as c
  import d from e

  import_aliases['c'] = a.b
  import_aliases['e'] = d.e # new behavior

  c() # qualified as a.b
  e() # qualified as d.e

To fix an imported symbol that is not aliased is still added
to import_aliases with its real name. The primary danger
is the code overriding the imported symbol with a local
name definition. We don't catch that, but it is less common.

This causes tests for os_popen, mktemp, and ssl-insecure-verion
to fail. In the first two tests, additional calls are identified
as problems. For the ssl test, we updated the test to look for
the fully qualified name pyOpenSSL.SSL.Context.

For other tests:
examples/os-popen.py:
  master was missing line 7 'popen()' which was imported from os.
  We can now catch this.

examples/mktemp.py:
  master was missing line 10 'mktemp(foo)' which was imported from
  tempfile. The blacklist specified tempfile.mktemp so this was missed.

Change-Id: I487bace2e4c9daaf5ab75fc8e351c3b990532e3a
This commit is contained in:
Lucas Fisher
2015-01-29 06:31:20 -08:00
parent 5d3a82d764
commit d70ec0d634
3 changed files with 14 additions and 4 deletions

View File

@@ -139,10 +139,20 @@ class BanditNodeVisitor(ast.NodeVisitor):
return self.visit_Import(node)
for nodename in node.names:
# TODO(ljfisher) Names in import_aliases could be overridden
# by local definitions. If this occurs bandit will see the
# name in import_aliases instead of the local definition.
# We need better tracking of names.
if nodename.asname:
self.context['import_aliases'][nodename.asname] = (
module + "." + nodename.name
)
else:
# Even if import is not aliased we need an entry that maps
# name to module.name. For example, with 'from a import b'
# b should be aliased to the qualified name a.b
self.context['import_aliases'][nodename.name] = (module + '.' +
nodename.name)
self.context['imports'].add(module + "." + nodename.name)
self.context['module'] = module
self.context['name'] = nodename.name

View File

@@ -32,7 +32,7 @@ def ssl_with_bad_version(context, config):
return(bandit.ERROR, 'ssl.wrap_socket call with insecure SSL/TLS'
' protocol version identified, security issue. %s' %
context.call_args_string)
elif (context.call_function_name_qual == 'SSL.Context'):
elif (context.call_function_name_qual == 'pyOpenSSL.SSL.Context'):
if context.check_call_arg_value('method') in bad_ssl_versions:
return(bandit.ERROR, 'SSL.Context call with insecure SSL/TLS'
@@ -40,7 +40,7 @@ def ssl_with_bad_version(context, config):
context.call_args_string)
elif (context.call_function_name_qual != 'ssl.wrap_socket' and
context.call_function_name_qual != 'SSL.Context'):
context.call_function_name_qual != 'pyOpenSSL.SSL.Context'):
if (context.check_call_arg_value('method') in bad_ssl_versions or
context.check_call_arg_value('ssl_version') in bad_ssl_versions):

View File

@@ -116,7 +116,7 @@ class FunctionalTests(unittest.TestCase):
def test_mktemp(self):
path = os.path.join(os.getcwd(), 'examples', 'mktemp.py')
self.b_mgr.run_scope([path])
self.assertEqual(self.b_mgr.scores[0], 15)
self.assertEqual(self.b_mgr.scores[0], 20)
def test_nonesense(self):
path = os.path.join(os.getcwd(), 'examples', 'nonesense.py')
@@ -136,7 +136,7 @@ class FunctionalTests(unittest.TestCase):
def test_os_popen(self):
path = os.path.join(os.getcwd(), 'examples', 'os-popen.py')
self.b_mgr.run_scope([path])
self.assertEqual(self.b_mgr.scores[0], 15)
self.assertEqual(self.b_mgr.scores[0], 20)
def test_os_spawn(self):
path = os.path.join(os.getcwd(), 'examples', 'os-spawn.py')