This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 18/22] Add checkers/flawfinder.py
- From: David Malcolm <dmalcolm at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Fri, 4 Aug 2017 18:04:49 -0400
- Subject: [PATCH 18/22] Add checkers/flawfinder.py
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx07.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx07.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=dmalcolm at redhat dot com
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com EA052C047B74
- References: <1501884293-9047-1-git-send-email-dmalcolm@redhat.com>
This patch adds a harness for invoking flawfinder:
https://www.dwheeler.com/flawfinder/
returning the results in JSON format.
It runs "flawfinder", then uses firehose.parsers.flawfinder.parse_file
to parse the stdout, turning it into firehose JSON.
checkers/ChangeLog:
* flawfinder.py: New file.
---
checkers/flawfinder.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)
create mode 100755 checkers/flawfinder.py
diff --git a/checkers/flawfinder.py b/checkers/flawfinder.py
new file mode 100755
index 0000000..475a513
--- /dev/null
+++ b/checkers/flawfinder.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# Copyright 2012, 2013, 2015, 2017 David Malcolm <dmalcolm@redhat.com>
+# Copyright 2012, 2013, 2015, 2017 Red Hat, Inc.
+#
+# This is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+import tempfile
+
+from gccinvocation import GccInvocation
+
+from checker import Checker, CheckerTests, make_file, make_stats, \
+ tool_main
+
+from firehose.model import Failure, Issue
+from firehose.parsers.flawfinder import parse_file
+
+class InvokeFlawfinder(Checker):
+ """
+ Checker subclass that invokes "flawfinder"
+ """
+ name = 'flawfinder'
+
+ def raw_invoke(self, gccinv, sourcefile):
+ args = ['flawfinder', sourcefile] # FIXME
+ return self._run_subprocess(sourcefile, args)
+
+ def handle_output(self, result):
+ if result.returncode:
+ analysis = self._make_failed_analysis(result.sourcefile, result.timer,
+ msgtext='Bad exit code running %s' % self.name,
+ failureid='bad-exit-code')
+ self.set_custom_fields(result, analysis)
+ return analysis
+
+ if 0:
+ print('result.err: %r' % result.err)
+ print('result.out: %r' % result.out)
+
+ # (there doesn't seem to be a way to have flawfinder directly
+ # save its output to a given location)
+
+ with tempfile.NamedTemporaryFile() as outfile:
+ outfile.write(result.out)
+ outfile.flush()
+
+ with open(outfile.name) as infile:
+ # Parse stderr into firehose XML format and save:
+ analysis = parse_file(infile)
+ analysis.metadata.file_ = make_file(result.sourcefile)
+ analysis.metadata.stats = make_stats(result.timer)
+ self.set_custom_fields(result, analysis)
+
+ return analysis
+
+ def set_custom_fields(self, result, analysis):
+ analysis.set_custom_field('flawfinder-invocation',
+ ' '.join(result.argv))
+ result.set_custom_fields(analysis)
+
+class FlawfinderTests(CheckerTests):
+ def make_tool(self):
+ return self.make_tool_from_class(InvokeFlawfinder)
+
+ def verify_basic_metadata(self, analysis, sourcefile):
+ # Verify basic metadata:
+ self.assert_metadata(analysis, 'flawfinder', sourcefile)
+ self.assert_has_custom_field(analysis, 'flawfinder-invocation')
+ self.assert_has_custom_field(analysis, 'stdout')
+ self.assert_has_custom_field(analysis, 'stderr')
+
+ def test_file_not_found(self):
+ analysis = self.invoke('does-not-exist.c')
+ #print(analysis)
+ self.assertEqual(len(analysis.results), 0)
+
+ def test_timeout(self):
+ sourcefile = 'test-sources/harmless.c'
+ tool = self.make_tool()
+ tool.timeout = 0
+ gccinv = GccInvocation(['gcc', sourcefile])
+ analysis = tool.checked_invoke(gccinv, sourcefile)
+ self.assert_metadata(analysis, 'flawfinder', sourcefile)
+ self.assertEqual(len(analysis.results), 1)
+ r0 = analysis.results[0]
+ self.assertIsInstance(r0, Failure)
+ self.assertEqual(r0.failureid, 'timeout')
+ self.assert_has_custom_field(analysis, 'timeout')
+ self.assert_has_custom_field(analysis, 'command-line')
+
+ def test_harmless_file(self):
+ analysis = self.invoke('test-sources/harmless.c')
+ self.assertEqual(len(analysis.results), 0)
+
+ def test_use_of_random(self):
+ analysis = self.invoke('test-sources/cpychecker-demo.c')
+ self.assertEqual(len(analysis.results), 1)
+ r0 = analysis.results[0]
+ self.assertIsInstance(r0, Issue)
+ self.assertEqual(r0.testid, 'random')
+ self.assertEqual(r0.location.file.givenpath,
+ 'test-sources/cpychecker-demo.c')
+ self.assertEqual(r0.location.point.line, 97)
+ self.assertEqual(r0.message.text,
+ "This function is not sufficiently random for"
+ " security-related functions such as key and nonce"
+ " creation. use a more secure technique for"
+ " acquiring random values.")
+ self.assertEqual(r0.severity, '3')
+
+if __name__ == '__main__':
+ sys.exit(tool_main(sys.argv, InvokeFlawfinder))
--
1.8.5.3