Add capability to pipe a file into bandit

Allows someone to feed a file/text into bandit from a pipe rather
than just the 'targets' argument.

Usage example:
   cat examples/imports.py | bandit -

Change-Id: I1566684c0ae5476374960095816cb1720ff465a2
This commit is contained in:
Eric Brown 2016-11-21 14:23:53 -08:00
parent 3cf14c773e
commit aae396e9bc
4 changed files with 62 additions and 45 deletions

View File

@ -73,6 +73,11 @@ using only the plugins listed in the ``ShellInjection`` profile::
bandit examples/*.py -p ShellInjection
Bandit also supports passing lines of code to scan using standard input. To
run Bandit with standard input::
cat examples/imports.py | bandit -
Usage::
$ bandit -h

View File

@ -222,48 +222,12 @@ class BanditManager():
sys.stderr.write("%s.. " % count)
sys.stderr.flush()
try:
with open(fname, 'rb') as fdata:
try:
# parse the current file
data = fdata.read()
lines = data.splitlines()
self.metrics.begin(fname)
self.metrics.count_locs(lines)
if self.ignore_nosec:
nosec_lines = set()
else:
nosec_lines = set(
lineno + 1 for
(lineno, line) in enumerate(lines)
if b'#nosec' in line or b'# nosec' in line)
score = self._execute_ast_visitor(fname, data,
nosec_lines)
self.scores.append(score)
self.metrics.count_issues([score, ])
except KeyboardInterrupt as e:
sys.exit(2)
except SyntaxError as e:
self.skipped.append((
fname,
"syntax error while parsing AST from file"
))
new_files_list.remove(fname)
except Exception as e:
LOG.error(
"Exception occurred when executing tests against "
"%s. Run \"bandit --debug %s\" to see the full "
"traceback.", fname, fname
)
self.skipped.append(
(fname, 'exception while scanning file')
)
new_files_list.remove(fname)
LOG.debug(" Exception string: %s", e)
LOG.debug(
" Exception traceback: %s",
traceback.format_exc()
)
continue
if fname == '-':
sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0)
self._parse_file('<stdin>', sys.stdin, new_files_list)
else:
with open(fname, 'rb') as fdata:
self._parse_file(fname, fdata, new_files_list)
except IOError as e:
self.skipped.append((fname, e.strerror))
new_files_list.remove(fname)
@ -278,6 +242,38 @@ class BanditManager():
# do final aggregation of metrics
self.metrics.aggregate()
def _parse_file(self, fname, fdata, new_files_list):
try:
# parse the current file
data = fdata.read()
lines = data.splitlines()
self.metrics.begin(fname)
self.metrics.count_locs(lines)
if self.ignore_nosec:
nosec_lines = set()
else:
nosec_lines = set(
lineno + 1 for
(lineno, line) in enumerate(lines)
if b'#nosec' in line or b'# nosec' in line)
score = self._execute_ast_visitor(fname, data, nosec_lines)
self.scores.append(score)
self.metrics.count_issues([score, ])
except KeyboardInterrupt as e:
sys.exit(2)
except SyntaxError as e:
self.skipped.append((fname,
"syntax error while parsing AST from file"))
new_files_list.remove(fname)
except Exception as e:
LOG.error("Exception occurred when executing tests against "
"%s. Run \"bandit --debug %s\" to see the full "
"traceback.", fname, fname)
self.skipped.append((fname, 'exception while scanning file'))
new_files_list.remove(fname)
LOG.debug(" Exception string: %s", e)
LOG.debug(" Exception traceback: %s", traceback.format_exc())
def _execute_ast_visitor(self, fname, data, nosec_lines):
'''Execute AST parse on each file

View File

@ -88,6 +88,11 @@ using only the plugins listed in the ShellInjection profile::
bandit examples/*.py -p ShellInjection
Bandit also supports passing lines of code to scan using standard input. To
run Bandit with standard input::
cat examples/imports.py | bandit -
SEE ALSO
========

View File

@ -21,10 +21,10 @@ import testtools
class RuntimeTests(testtools.TestCase):
def _test_runtime(self, cmdlist):
def _test_runtime(self, cmdlist, infile=None):
process = subprocess.Popen(
cmdlist,
stdin=subprocess.PIPE,
stdin=infile if infile else subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=True
@ -38,7 +38,6 @@ class RuntimeTests(testtools.TestCase):
cmdlist.append(os.path.join(os.getcwd(), 'examples', t))
return self._test_runtime(cmdlist)
# test direct execution of bandit
def test_no_arguments(self):
(retcode, output) = self._test_runtime(['bandit', ])
self.assertEqual(2, retcode)
@ -47,6 +46,18 @@ class RuntimeTests(testtools.TestCase):
else:
self.assertIn("arguments are required: targets", output)
def test_piped_input(self):
with open('examples/imports.py', 'r') as infile:
(retcode, output) = self._test_runtime(['bandit', '-'], infile)
self.assertEqual(1, retcode)
self.assertIn("Total lines of code: 4", output)
self.assertIn("Low: 2", output)
self.assertIn("High: 2", output)
self.assertIn("Files skipped (0):", output)
self.assertIn("Issue: [B403:blacklist] Consider possible", output)
self.assertIn("<stdin>:2", output)
self.assertIn("<stdin>:4", output)
def test_nonexistent_config(self):
(retcode, output) = self._test_runtime([
'bandit', '-c', 'nonexistent.yml', 'xx.py'