예제 #1
0
def generate_fake_bases(output_dir):
    analyses = []
    for tool, tool_version in analysis_tools:
        for sut, sut_version in software:
            issues = []
            metadata = Metadata(Generator(tool, tool_version),
                                DebianSource(sut, sut_version, None),
                                None, None)
            for i in range(random.randint(0, 4)):
                file_, function = functions[random.randint(0, len(functions)-1)]
                file_ = File(file_, None, None)
                function = Function(function)
                location = Location(file_, function)
                try:
                    fortune = subprocess.check_output(["fortune"])
                except OSError:
                    print("'fortune' is not installed and required to"
                          "generate fake messages")
                    import sys; sys.exit()
                message = Message(fortune)
                issue = Issue(None, None, location, message, None, None)
                issues.append(issue)
            analysis = Analysis(metadata, issues)
            analyses.append(analysis)
    i = 0
    for analysis in analyses:
        f = open(os.path.join(output_dir, "analysis" + str(i) + ".xml"), "w")
        f.write(analysis.to_xml_bytes())
        f.write("\n")

        f.close()
        i += 1
예제 #2
0
def parse_file(data_file_name,
               findbugs_version=None,
               sut=None,
               file_=None,
               stats=None):
    """
    :param data_file:           str object containing findbugs scan result
    :type  data_file:           str
    :param findbugs_version:    version of findbugs
    :type  findbugs_version:    str

    :return:    Analysis instance
    """
    data_file = open(data_file_name)
    generator = Generator(name="findbugs", version=findbugs_version)
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])
    for line in data_file.readlines():
        issue = parse_line(line)
        if issue:
            analysis.results.append(issue)
        else:
            sys.stderr.write("fail to pass line=[%s]" % line)
    data_file.close()
    return analysis
    def test_multiple_source_files(self):
        """
        Verify invocations that cover multiple source files, linking
        to an executable.
        """
        driver = self.make_driver()
        args = ['gcc',
                'test-sources/multiple-1.c',
                'test-sources/multiple-2.c',
                '-o', 'multiple.exe']
        r = driver.invoke(args)
        self.assertEqual(driver.ctxt.stderr.getvalue(), '')
        self.assertEqual(driver.returncode, 0)

        # Verify that it wrote out "firehose" XML files to 'outputdir':
        analyses = []
        for xmlpath in glob.glob(os.path.join(driver.outputdir, '*.xml')):
            with open(xmlpath) as f:
                analysis = Analysis.from_xml(f)
            analyses.append(analysis)
            # Verify that none of the side-effects failed (e.g. with
            # linker errors):
            self.assertEqual(analysis.customfields['returncode'], 0)
            os.unlink(xmlpath)
        self.assertEqual(len(analyses), 8)

        # We should have a ./multiple.exe that we can run
        self.assertTrue(os.path.exists('./multiple.exe'))
        os.unlink('./multiple.exe')

        os.rmdir(driver.outputdir)
    def invoke(self, sourcefile, extraargs=None):
        driver = self.make_driver()
        args = ['gcc', '-c', sourcefile]
        if extraargs:
            args += extraargs
        r = driver.invoke(args)

        if sourcefile and os.path.exists(sourcefile):
            with open(sourcefile) as f:
                content = f.read()
            expected_hexdigest = hashlib.sha1(content).hexdigest()
        else:
            expected_hexdigest = None

        # Verify that it wrote out "firehose" XML files to 'outputdir':
        analyses = []
        for xmlpath in glob.glob(os.path.join(driver.outputdir, '*.xml')):
            with open(xmlpath) as f:
                analysis = Analysis.from_xml(f)
            if 0:
                print(analysis)
            if expected_hexdigest:
                self.assertEqual(analysis.metadata.file_.hash_.alg, 'sha1')
                self.assertEqual(analysis.metadata.file_.hash_.hexdigest,
                                 expected_hexdigest)
            analyses.append(analysis)
            os.unlink(xmlpath)
        self.assertEqual(len(analyses), 4)

        os.rmdir(driver.outputdir)

        return driver
예제 #5
0
def parse_plist(pathOrFile,
                analyzerversion=None,
                sut=None,
                file_=None,
                stats=None):
    """
    Given a .plist file emitted by clang-static-analyzer (e.g. via
    scan-build), parse it and return an Analysis instance
    """
    plist = plistlib.readPlist(pathOrFile)
    # We now have the .plist file as a hierarchy of dicts, lists, etc

    # Handy debug dump:
    if 0:
        pprint(plist)

    # A list of filenames, apparently referenced by index within
    # diagnostics:
    files = plist['files']

    generator = Generator(name='clang-analyzer', version=analyzerversion)
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])

    for diagnostic in plist['diagnostics']:
        if 0:
            pprint(diagnostic)

        cwe = None

        # TODO: we're not yet handling the following:
        #   diagnostic['category']
        #   diagnostic['type']

        message = Message(text=diagnostic['description'])

        loc = diagnostic['location']
        location = Location(
            file=File(givenpath=files[loc.file], abspath=None),

            # FIXME: doesn't tell us function name
            # TODO: can we patch this upstream?
            function=None,
            point=Point(int(loc.line), int(loc.col)))

        notes = None

        trace = make_trace(files, diagnostic['path'])

        issue = Issue(
            cwe,
            None,  # FIXME: can we get at the test id?
            location,
            message,
            notes,
            trace)

        analysis.results.append(issue)

    return analysis
예제 #6
0
def convert_reports_to_firehose():
    reports = []

    # scan-build (clang analyzer)
    scanbuild_report_results = []
    metadata = None
    for resultdir in glob.glob(os.path.join('reports/scan-build', '*')):
        scanbuild_tmp_firehose_report = clanganalyzer.parse_scandir(resultdir)
        for report in scanbuild_tmp_firehose_report:
            metadata = report.metadata
            scanbuild_report_results += report.results
    scanbuild_firehose_report = Analysis(metadata, scanbuild_report_results)
    reports.append(scanbuild_firehose_report)

    # framac
    with open('reports/frama-c/frama-c.log') as framac_raw_report:
        framac_firehose_report = frama_c.parse_file(framac_raw_report)
        reports.append(framac_firehose_report)

    # flawfinder
    with open('reports/flawfinder/flawfinder.log') as flawfinder_raw_report:
        flawfinder_firehose_report = flawfinder.parse_file(flawfinder_raw_report)
        reports.append(flawfinder_firehose_report)

    # cppcheck
    with open('reports/cppcheck/cppcheck.log') as cppcheck_raw_report:
        cppcheck_firehose_report = cppcheck.parse_file(cppcheck_raw_report)
        reports.append(cppcheck_firehose_report)

    os.makedirs('reports/firehose')
    for report in reports:
        tool_name = report.metadata.generator.name
        fh_report_path = 'reports/firehose/' + tool_name + '.xml'
        with open(fh_report_path, 'wb') as fh_report:
            report.to_xml().write(fh_report, encoding='utf-8')
예제 #7
0
def parse_coccinelle(lines):
    # we have to emulate a file for from_xml()
    firehose = StringIO.StringIO(lines)
    analysis = Analysis.from_xml(firehose)

    for result in analysis.results:
        yield result
예제 #8
0
def invoke_side_effects(argv):
    log("invoke_side_effects: %s"
        % ' '.join(sys.argv))

    gccinv = GccInvocation(argv)

    # Try to run each side effect in a subprocess, passing in a path
    # for the XML results to be written to.
    # Cover a multitude of possible failures by detecting if no output
    # was written, and capturing *that* as a failure
    for sourcefile in gccinv.sources:
        if sourcefile.endswith('.c'): # FIXME: other extensions?
            for script, genname in [('invoke-cppcheck', 'cppcheck'),
                                    ('invoke-clang-analyzer', 'clang-analyzer'),
                                    ('invoke-cpychecker', 'cpychecker'),

                                    # Uncomment the following to test a
                                    # checker that fails to write any XML:
                                    # ('echo', 'failing-checker'),

                                    ]:
                with tempfile.NamedTemporaryFile() as f:
                    dstxmlpath = f.name
                assert not os.path.exists(dstxmlpath)

                # Restrict the invocation to just one source file at a
                # time:
                singleinv = gccinv.restrict_to_one_source(sourcefile)
                singleargv = singleinv.argv

                TIMEOUT=60
                t = Timer()

                args = [script, dstxmlpath] + singleargv
                log('invoking args: %r' % args)
                p = Popen(args,
                          stdout=PIPE, stderr=PIPE)
                try:
                    out, err = p.communicate(timeout=TIMEOUT)
                    write_streams(script, out, err)

                    if os.path.exists(dstxmlpath):
                        with open(dstxmlpath) as f:
                            analysis = Analysis.from_xml(f)
                    else:
                        analysis = make_failed_analysis(genname, sourcefile, t,
                                                        msgtext=('Unable to locate XML output from %s'
                                                                 % script),
                                                        failureid='no-output-found')
                        analysis.set_custom_field('stdout', out)
                        analysis.set_custom_field('stderr', err)
                        analysis.set_custom_field('returncode', p.returncode)
                except TimeoutExpired:
                    analysis = make_failed_analysis(genname, sourcefile, t,
                                                    msgtext='Timeout running %s' % genname,
                                                    failureid='timeout')
                    analysis.set_custom_field('timeout', TIMEOUT)
                analysis.set_custom_field('gcc-invocation', ' '.join(argv))
                write_analysis_as_xml(analysis)
예제 #9
0
def parse_file(fileobj, sut=None, file_=None, stats=None):
    tree = ET.parse(fileobj)
    root = tree.getroot()
    node_cppcheck = root.find('cppcheck')
    version = node_cppcheck.get('version')
    node_errors = root.find('errors')

    generator = Generator(name='cppcheck',
                          version=node_cppcheck.get('version'))
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])

    for node_error in node_errors.findall('error'):
        # e.g.:
        # <error id="nullPointer" severity="error" msg="Possible null pointer dereference: end - otherwise it is redundant to check it against null." verbose="Possible null pointer dereference: end - otherwise it is redundant to check it against null.">
        #  <location file="python-ethtool/ethtool.c" line="139"/>
        #  <location file="python-ethtool/ethtool.c" line="141"/>
        # </error>
        testid = node_error.get('id')
        str_msg = node_error.get('msg')
        str_verbose = node_error.get('verbose')
        message = Message(text=str_msg)
        if str_verbose != str_msg:
            notes = Notes(str_verbose)
        else:
            notes = None

        location_nodes = list(node_error.findall('location'))
        for node_location in location_nodes:
            location = Location(
                file=File(node_location.get('file'), None),

                # FIXME: doesn't tell us function name
                # TODO: can we patch this upstream?
                function=None,

                # doesn't emit column
                point=Point(int(node_location.get('line')),
                            0))  # FIXME: bogus column
            issue = Issue(None,
                          testid,
                          location,
                          message,
                          notes,
                          None,
                          severity=node_error.get('severity'))
            analysis.results.append(issue)

        if not location_nodes:
            customfields = CustomFields()
            if str_verbose != str_msg:
                customfields['verbose'] = str_verbose
            failure = Failure(failureid=testid,
                              location=None,
                              message=message,
                              customfields=customfields)
            analysis.results.append(failure)

    return analysis
예제 #10
0
 def test_from_xml(self):
     num_analyses = 0
     for filename in sorted(glob.glob('examples/example-*.xml')):
         with open(filename) as f:
             r = Analysis.from_xml(f)
             num_analyses += 1
     # Ensure that all of the reports were indeed parsed:
     self.assertEqual(num_analyses, 9)
예제 #11
0
 def test_from_xml(self):
     num_analyses = 0
     for filename in sorted(glob.glob('examples/example-*.xml')):
         with open(filename) as f:
             r = Analysis.from_xml(f)
             num_analyses += 1
     # Ensure that all of the reports were indeed parsed:
     self.assertEqual(num_analyses, 10)
예제 #12
0
class Pep8TestCase(unittest.TestCase):
    filepath = "tests/resources/python-firehose_0.3-1.dsc"
    firehose_results = Analysis(
        metadata=Metadata(
            generator=Generator(
                name='pep8'
                ),
            sut=None,
            file_=None,
            stats=None),
        results=[]
        )


    @mock.patch('debile.slave.runners.pep8.run_command',
            return_value=('1.5.7', '', 0))
    def test_pep8_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'pep8')
        self.assertEquals(ver, '1.5.7')


    @mock.patch('debile.slave.runners.pep8.run_command',
            return_value=('1.5.7', '', 1))
    def test_pep8_version(self, mock):
        self.assertRaises(Exception, version)


    def test_pep8(self):
        pep8_analysis = pep8(self.filepath, self.firehose_results)
        content = pep8_analysis[1]
        self.assertTrue("missing whitespace around operator" in content)

        # It think it is safe to say that the string is not 4 chars long
        self.assertTrue(len(content) > 4)


    def test_pep8_wrappers(self):
        pep8_analysis = pep8(self.filepath, self.firehose_results)
        issues = parse_pep8(pep8_analysis[1].splitlines())
        i = 0
        for issue in issues:
            if issue.location.file.givenpath == "./firehose/model.py" and \
            issue.location.point.line==96 and issue.location.point.column==1:
                found = issue
            i += 1
        print found
        self.assertEquals(found.testid, "E302")
        self.assertEquals(found.location.file.givenpath, "./firehose/model.py")
        self.assertEquals(found.location.point.line, 96)
        self.assertEquals(found.location.point.column, 1)
        self.assertEquals(found.severity, "error")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue("E302 expected 2 blank lines, found 1" in found.message.text)
        self.assertTrue(i > 100)
예제 #13
0
    def roundtrip_example(self, filename):
        fullpath = os.path.join('../firehose/examples', filename)  # FIXME
        a = Analysis.from_xml(fullpath)
        self.session.add(a)
        self.session.commit()

        session2 = self.Session()
        b = session2.query(Analysis).first()
        #print(b)
        self.assertEqual(a, b)
예제 #14
0
def test_cppcheck_common():
    firehorse_results = Analysis(
        metadata=Metadata(generator=Generator(name='cppcheck'),
                          sut=None,
                          file_=None,
                          stats=None),
        results=[])

    return cppcheck("tests/resources/libjsoncpp_0.6.0~rc2-3.1.dsc",
                    firehorse_results)
예제 #15
0
def get_labeled_reports(*exclude):
    results = []
    firehose_reports = glob.glob(os.path.join('reports', 'firehose', 'labeled_reports', '*.xml'))
    for analyzer in exclude:
        if analyzer == 'scan-build':
            analyzer = 'clang-analyzer'
        firehose_reports.remove('reports/firehose/labeled_reports/%s.xml' % analyzer)
    for fh_xml_file in firehose_reports:
        results.append(Analysis.from_xml(fh_xml_file))
    return results
예제 #16
0
    def roundtrip_example(self, filename):
        fullpath = os.path.join('../firehose/examples', filename) # FIXME
        a = Analysis.from_xml(fullpath)
        self.session.add(a)
        self.session.commit()

        session2 = self.Session()
        b = session2.query(Analysis).first()
        #print(b)
        self.assertEqual(a, b)
예제 #17
0
 def test_debian_source(self):
     """ Test to ensure that Debian source package Sut loading works. """
     with open('examples/example-debian-source.xml') as f:
         a = Analysis.from_xml(f)
         self.assertEqual(a.metadata.generator.name, 'handmade')
         self.assertEqual(a.metadata.generator.version, '0.1')
         self.assertIsInstance(a.metadata.sut, DebianSource)
         self.assertEqual(a.metadata.sut.name, 'python-ethtool')
         self.assertEqual(a.metadata.sut.version, '0.7')
         self.assertEqual(a.metadata.sut.release, '4.1+b1')
         self.assertFalse(hasattr(a.metadata.sut, 'buildarch'))
예제 #18
0
 def test_debian_binary(self):
     """ Test to ensure that Debian binary package Sut loading works. """
     with open('examples/example-debian-binary.xml') as f:
         a = Analysis.from_xml(f)
         self.assertEqual(a.metadata.generator.name, 'handmade')
         self.assertEqual(a.metadata.generator.version, '0.1')
         self.assertIsInstance(a.metadata.sut, DebianBinary)
         self.assertEqual(a.metadata.sut.name, 'python-ethtool')
         self.assertEqual(a.metadata.sut.version, '0.7')
         self.assertEqual(a.metadata.sut.buildarch, 'amd64')
         self.assertEqual(a.metadata.sut.release, '1.1')
예제 #19
0
 def test_debian_source(self):
     """ Test to ensure that Debian source package Sut loading works. """
     with open('examples/example-debian-source.xml') as f:
         a = Analysis.from_xml(f)
         self.assertEqual(a.metadata.generator.name, 'handmade')
         self.assertEqual(a.metadata.generator.version, '0.1')
         self.assertIsInstance(a.metadata.sut, DebianSource)
         self.assertEqual(a.metadata.sut.name, 'python-ethtool')
         self.assertEqual(a.metadata.sut.version, '0.7')
         self.assertEqual(a.metadata.sut.release, '4.1+b1')
         self.assertFalse(hasattr(a.metadata.sut, 'buildarch'))
예제 #20
0
 def test_debian_binary(self):
     """ Test to ensure that Debian binary package Sut loading works. """
     with open('examples/example-debian-binary.xml') as f:
         a = Analysis.from_xml(f)
         self.assertEqual(a.metadata.generator.name, 'handmade')
         self.assertEqual(a.metadata.generator.version, '0.1')
         self.assertIsInstance(a.metadata.sut, DebianBinary)
         self.assertEqual(a.metadata.sut.name, 'python-ethtool')
         self.assertEqual(a.metadata.sut.version, '0.7')
         self.assertEqual(a.metadata.sut.buildarch, 'amd64')
         self.assertEqual(a.metadata.sut.release, '1.1')
예제 #21
0
def parse_json_v2(path):
    """
    Given a JSON file emitted by:
      cov-format-errors --json-output-v2=<filename>
    parse it and return an Analysis instance
    """
    with open(path) as f:
        js = json.load(f)
    if 0:
        pprint(js)

    generator = Generator(name='coverity')
    metadata = Metadata(generator, sut=None, file_=None, stats=None)
    analysis = Analysis(metadata, [])

    for issue in js['issues']:
        if 0:
            pprint(issue)

        cwe = None

        # Use checkerName (e.g. "RESOURCE_LEAK") for
        # the testid:
        testid = issue['checkerName']

        # Use the eventDescription of the final event for the message:
        message = Message(text=issue['events'][-1]['eventDescription'])

        location = Location(
            file=File(givenpath=issue['mainEventFilePathname'], abspath=None),
            function=Function(name=issue['functionDisplayName']),
            point=Point(int(issue['mainEventLineNumber']), int(0)))

        notes = None

        trace = make_trace(issue)

        customfields = CustomFields()
        for key in ['mergeKey', 'subcategory', 'domain']:
            if key in issue:
                customfields[key] = issue[key]

        issue = Issue(cwe,
                      testid,
                      location,
                      message,
                      notes,
                      trace,
                      customfields=customfields)

        analysis.results.append(issue)

    return analysis
예제 #22
0
class PylintTestCase(unittest.TestCase):
    filepath = "tests/resources/python-firehose_0.3-1.dsc"
    firehose_results = Analysis(metadata=Metadata(
        generator=Generator(name='pylint'), sut=None, file_=None, stats=None),
                                results=[])

    @mock.patch(
        'debile.slave.runners.pylint.run_command',
        return_value=(
            'pylint 1.4.3,\nastroid 1.3.6, common 0.62.0\nPython 2.7.10', '',
            0))
    def test_pylint_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'pylint')
        self.assertEquals(ver, '1.4.3')

    @mock.patch('debile.slave.runners.pylint.run_command',
                return_value=('', '', 1))
    def test_pylint_version_with_error(self, mock):
        self.assertRaises(Exception, version)

    def test_pylint(self):
        pylint_analysis = pylint(self.filepath, self.firehose_results)
        content = pylint_analysis[1]
        self.assertTrue("Missing method docstring" in content)

        # It think it is safe to say that the string is not 4 chars long
        self.assertTrue(len(content) > 4)

    def test_pylint_wrappers(self):
        pylint_analysis = pylint(self.filepath, self.firehose_results)
        issues = parse_pylint(pylint_analysis[1].splitlines())
        i = 0
        for issue in issues:
            if issue.location.file.givenpath == \
                    "tests/parsers/test_clanganalyzer_parser.py" and \
            issue.location.point.line==22 and issue.location.point.column==0:
                found = issue
            i += 1
        print found
        self.assertEquals(found.testid, "W0611")
        self.assertEquals(found.location.file.givenpath,
                          "tests/parsers/test_clanganalyzer_parser.py")
        self.assertEquals(found.location.point.line, 22)
        self.assertEquals(found.location.point.column, 0)
        self.assertEquals(found.severity, "warning")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue(
            "[unused-import]Unused Analysis imported from firehose.model" in
            found.message.text)
        self.assertTrue(i > 500)
예제 #23
0
class Flake8TestCase(unittest.TestCase):
    filepath = "tests/resources/python-firehose_0.3-1.dsc"
    firehose_results = Analysis(metadata=Metadata(
        generator=Generator(name='flake8'), sut=None, file_=None, stats=None),
                                results=[])

    @mock.patch(
        'debile.slave.runners.flake8.run_command',
        return_value=
        ('2.2.2 (pep8: 1.5.7, mccabe: 0.2.1, pyflakes: 0.8.1) CPython 2.7.10 on Linux',
         '', 0))
    def test_flake8_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'flake8')
        self.assertEquals(ver, '2.2.2')

    @mock.patch('debile.slave.runners.flake8.run_command',
                return_value=('', '', 1))
    def test_flake8_version_with_error(self, mock):
        self.assertRaises(Exception, version)

    def test_flake8(self):
        flake8_analysis = flake8(self.filepath, self.firehose_results)
        content = flake8_analysis[1]
        self.assertTrue("missing whitespace around operator" in content)

        # It think it is safe to say that the string is not 4 chars long
        self.assertTrue(len(content) > 4)

    def test_flake8_wrappers(self):
        flake8_analysis = flake8(self.filepath, self.firehose_results)
        issues = parse_flake8(flake8_analysis[1].splitlines())
        i = 0
        for issue in issues:
            if issue.location.file.givenpath == "./firehose/parsers/cppcheck.py" and \
            issue.location.point.line==37 and issue.location.point.column==5:
                found = issue
            i += 1
        print found
        self.assertEquals(found.testid, "F841")
        self.assertEquals(found.location.file.givenpath,
                          "./firehose/parsers/cppcheck.py")
        self.assertEquals(found.location.point.line, 37)
        self.assertEquals(found.location.point.column, 5)
        self.assertEquals(found.severity, "error")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue(
            "F841 local variable 'version' is assigned to but never used" in
            found.message.text)
        self.assertTrue(i > 100)
예제 #24
0
def create_firehose(package, version_getter):
    logging.info("Initializing empty firehose report")
    sut = {
        "sources": generate_sut_from_source,
        "binaries": generate_sut_from_binary
    }[package['_type']](package)

    gname_, gversion = version_getter()
    gname = "ethel/%s" % gname_

    return Analysis(metadata=Metadata(
        generator=Generator(name=gname, version=gversion),
        sut=sut, file_=None, stats=None), results=[])
예제 #25
0
    def test_example_6(self):
        with open('examples/example-6.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Failure)
            self.assertEqual(w.failureid, 'too-complicated')
            self.assertEqual(w.message.text,
                             'this function is too complicated for the'
                             ' reference-count checker to fully analyze:'
                             ' not all paths were analyzed')
            self.assertEqual(w.customfields, None)
예제 #26
0
    def test_example_6(self):
        with open('examples/example-6.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Failure)
            self.assertEqual(w.failureid, 'too-complicated')
            self.assertEqual(
                w.message.text, 'this function is too complicated for the'
                ' reference-count checker to fully analyze:'
                ' not all paths were analyzed')
            self.assertEqual(w.customfields, None)
예제 #27
0
def simple_tests():
    ex = Analysis.from_xml('../firehose/examples/example-1.xml')
    print(ex)
    session.add(ex)
    print(ex.id)
    print(session.query(Analysis).first())
    print(session.query(Failure).first())
    print(ex.id)
    session.commit()

    # Do it from a new session:
    session2 = Session()
    print(session2.query(Analysis).first())
    print(session2.query(Failure).first())
예제 #28
0
    def test_example_2(self):
        # Verify that the parser works:
        with open('examples/example-2.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')
            self.assertEqual(a.metadata.generator.version, '0.11')
            self.assertIsInstance(a.metadata.sut, SourceRpm)
            self.assertEqual(a.metadata.sut.name, 'python-ethtool')
            self.assertEqual(a.metadata.sut.version, '0.7')
            self.assertEqual(a.metadata.sut.release, '4.fc19')
            self.assertEqual(a.metadata.sut.buildarch, 'x86_64')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Issue)
            self.assertEqual(w.cwe, 401)
            self.assertEqual(w.testid, 'refcount-too-high')
            self.assertEqual(w.location.file.givenpath,
                             'examples/python-src-example.c')
            self.assertEqual(w.location.file.abspath, None)
            self.assertEqual(w.location.file.hash_.alg, 'sha1')
            self.assertEqual(w.location.file.hash_.hexdigest,
                             '6ba29daa94d64b48071e299a79f2a00dcd99eeb1')
            self.assertEqual(w.location.function.name,
                             'make_a_list_of_random_ints_badly')
            self.assertEqual(w.location.line, 40)
            self.assertEqual(w.location.column, 4)
            self.assertEqual(w.message.text,
                             "ob_refcnt of '*item' is 1 too high")
            self.assertMultiLineEqual(w.notes.text, (
                "was expecting final item->ob_refcnt to be N + 1 (for some unknown N)\n"
                "due to object being referenced by: PyListObject.ob_item[0]\n"
                "but final item->ob_refcnt is N + 2"))

            self.assertIsInstance(w.trace, Trace)
            self.assertEqual(len(w.trace.states), 3)
            s0 = w.trace.states[0]
            self.assertIsInstance(s0, State)
            self.assertEqual(s0.location.file.givenpath,
                             'examples/python-src-example.c')
            self.assertEqual(s0.location.function.name,
                             'make_a_list_of_random_ints_badly')
            self.assertEqual(s0.location.line, 36)
            self.assertEqual(s0.location.column, 14)
            self.assertEqual(
                s0.notes.text,
                'PyLongObject allocated at:         item = PyLong_FromLong(random());'
            )
예제 #29
0
 def make_info(self):
     a = Analysis(
         metadata=Metadata(generator=Generator(name='an-invented-checker'),
                           sut=None,
                           file_=None,
                           stats=None),
         results=[
             Info(infoid='gimple-stats',
                  location=Location(file=File('bar.c', None),
                                    function=Function('sample_function'),
                                    point=Point(10, 15)),
                  message=Message('sample message'),
                  customfields=CustomFields(num_stmts=57,
                                            num_basic_blocks=10))
         ])
     return a, a.results[0]
예제 #30
0
 def make_complex_analysis(self):
     """
     Construct a Analysis instance that uses all features
     """
     a = Analysis(
         metadata=Metadata(generator=Generator(name='cpychecker',
                                               version='0.11'),
                           sut=SourceRpm(name='python-ethtool',
                                         version='0.7',
                                         release='4.fc19',
                                         buildarch='x86_64'),
                           file_=File(givenpath='foo.c',
                                      abspath='/home/david/coding/foo.c'),
                           stats=Stats(wallclocktime=0.4)),
         results=[
             Issue(cwe=681,
                   testid='refcount-too-high',
                   location=Location(file=File(
                       givenpath='foo.c',
                       abspath='/home/david/coding/foo.c'),
                                     function=Function('bar'),
                                     point=Point(10, 15)),
                   message=Message(text='something bad involving pointers'),
                   notes=Notes('here is some explanatory text'),
                   trace=Trace([
                       State(location=Location(file=File('foo.c', None),
                                               function=Function('bar'),
                                               point=Point(7, 12)),
                             notes=Notes('first we do this')),
                       State(location=Location(file=File('foo.c', None),
                                               function=Function('bar'),
                                               point=Point(8, 10)),
                             notes=Notes('then we do that')),
                       State(location=Location(file=File('foo.c', None),
                                               function=Function('bar'),
                                               range_=Range(
                                                   Point(10, 15),
                                                   Point(10, 25))),
                             notes=Notes('then it crashes here'))
                   ]),
                   severity='really bad',
                   customfields=CustomFields(foo='bar')),
         ],
         customfields=CustomFields(
             gccinvocation='gcc -I/usr/include/python2.7 -c foo.c'),
     )
     return a, a.results[0]
예제 #31
0
def parse_file(data_file_obj, findbugs_version=None, sut=None, file_=None,
        stats=None):
    """
    :param data_file_obj:       file object containing findbugs scan result
                                in xml format, it can be generated using 
                                command:
                                fb analyze -xml:withMessages [jar_file]
    :type  data_file_obj:       file object
    :param findbugs_version:    version of findbugs
    :type  findbugs_version:    str

    :return:    Analysis instance
    """
    generator = Generator(name = "findbugs",
            version = findbugs_version)
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])

    def parse_BugInstance(bugInstance):
        message = Message(bugInstance.find("LongMessage").text)
        # findbugs has no column information
        sourceLine = bugInstance.find("SourceLine")
        point = Point(int(sourceLine.get("start")), 0)
        path = sourceLine.get("sourcepath")
        path = File(path, None)
        method = bugInstance.find("Method")
        if method:
            function = method.find("Message").text
            tmpIndex = function.rfind("In method ") + len("In method ") - 1
            function = Function(function[tmpIndex+1:])
        else:
            function = None
        location = Location(path, function, point)
        if DEBUG:
            print(str(location)+" "+str(message))
        return Issue(None, None, location, message, None, None)

    tree = ET.parse(data_file_obj)
    root = tree.getroot()
    for bugInstance in root.findall("BugInstance"):
        issue=parse_BugInstance(bugInstance)
        if issue:
            analysis.results.append(issue)
        else:
            sys.stderr.write("fail to pass bugInstance=[%s]\n" %
                    str(bugInstance))
    return analysis
예제 #32
0
class LintianTestCase(unittest.TestCase):
    filepath = "tests/resources/libjsoncpp0_0.6.0~rc2-3.1_amd64.deb"
    firehose_results = Analysis(metadata=Metadata(
        generator=Generator(name='lintian'), sut=None, file_=None, stats=None),
                                results=[])

    @mock.patch('debile.slave.runners.lintian.run_command',
                return_value=('Lintian v2.5.31', '', 0))
    def test_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'Lintian')
        self.assertEquals(ver, 'v2.5.31')

    @mock.patch('debile.slave.runners.lintian.run_command',
                return_value=('Lintian v2.5.31', '', 1))
    def test_version_without_lintian(self, mock):
        self.assertRaises(Exception, version)

    def test_lintian(self):
        lintian_analysis = lintian(self.filepath, self.firehose_results)
        content = lintian_analysis[1]
        self.assertTrue("no-symbols-control-file" in content)

        # It think it is safe to say that the string is not 4 chars long
        self.assertTrue(len(content) > 4)

    def test_lintian_wrappers(self):
        lintian_analysis = lintian(self.filepath, self.firehose_results)
        issues = parse_lintian(lintian_analysis[1].splitlines(), self.filepath)
        i = 0
        for issue in issues:
            if issue.testid == "no-symbols-control-file":
                found = issue
            i += 1
        self.assertEquals(found.testid, "no-symbols-control-file")
        self.assertEquals(
            found.location.file.givenpath,
            "tests/resources/libjsoncpp0_0.6.0~rc2-3.1_amd64.deb")
        self.assertIsNone(found.location.point)
        self.assertEquals(found.severity, "info")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue(
            "libjsoncpp0: no-symbols-control-file usr/lib/libjsoncpp.so.0.6.0"
            in found.message.text)
        self.assertTrue(i > 1)
예제 #33
0
    def test_example_3(self):
        # Verify that the parser works:
        with open('examples/example-3.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')
            self.assertEqual(a.metadata.generator.version, '0.11')
            self.assertIsInstance(a.metadata.sut, SourceRpm)
            self.assertEqual(a.metadata.sut.name, 'python-ethtool')
            self.assertEqual(a.metadata.sut.version, '0.7')
            self.assertEqual(a.metadata.sut.release, '4.fc19')
            self.assertEqual(a.metadata.sut.buildarch, 'x86_64')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Failure)
            self.assertEqual(w.failureid, 'bad-exit-code')
            self.assertEqual(w.customfields['returncode'], -11)
예제 #34
0
    def test_example_3(self):
        # Verify that the parser works:
        with open('examples/example-3.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')
            self.assertEqual(a.metadata.generator.version, '0.11')
            self.assertIsInstance(a.metadata.sut, SourceRpm)
            self.assertEqual(a.metadata.sut.name, 'python-ethtool')
            self.assertEqual(a.metadata.sut.version, '0.7')
            self.assertEqual(a.metadata.sut.release, '4.fc19')
            self.assertEqual(a.metadata.sut.buildarch, 'x86_64')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Failure)
            self.assertEqual(w.failureid, 'bad-exit-code')
            self.assertEqual(w.customfields['returncode'], -11)
 def handle_output(self, result):
     if os.path.exists(self.outputxmlpath):
         with open(self.outputxmlpath) as f:
             analysis = Analysis.from_xml(f)
             analysis.metadata.file_ = make_file(result.sourcefile)
             analysis.metadata.stats = make_stats(result.timer)
     else:
         analysis = \
             self._make_failed_analysis(
                 result.sourcefile, result.timer,
                 msgtext=('Unable to locate XML output from %s'
                          % self.name),
                 failureid='no-output-found')
     analysis.set_custom_field('cpychecker-invocation',
                               ' '.join(result.argv))
     result.set_custom_fields(analysis)
     return analysis
예제 #36
0
 def make_failed_analysis(self):
     a = Analysis(
         metadata=Metadata(generator=Generator(name='yet-another-checker'),
                           sut=None,
                           file_=None,
                           stats=None),
         results=[
             Failure(failureid='out-of-memory',
                     location=Location(
                         file=File('foo.c', None),
                         function=Function('something_complicated'),
                         point=Point(10, 15)),
                     message=Message('out of memory'),
                     customfields=CustomFields(stdout='sample stdout',
                                               stderr='sample stderr',
                                               returncode=-9))  # (killed)
         ])
     return a, a.results[0]
예제 #37
0
    def test_example_5(self):
        # Ensure that we can load range information from XML
        with open('examples/example-5.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(len(a.results), 1)

            w = a.results[0]
            self.assertIsInstance(w, Issue)
            self.assertEqual(w.location.range_.start.line, 10)
            self.assertEqual(w.location.range_.start.column, 9)
            self.assertEqual(w.location.range_.end.line, 10)
            self.assertEqual(w.location.range_.end.column, 44)

            self.assertEqual(w.location.point, None)

            # The line/column getters use the start:
            self.assertEqual(w.location.line, 10)
            self.assertEqual(w.location.column, 9)
예제 #38
0
    def test_example_5(self):
        # Ensure that we can load range information from XML
        with open('examples/example-5.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(len(a.results), 1)

            w = a.results[0]
            self.assertIsInstance(w, Issue)
            self.assertEqual(w.location.range_.start.line, 10)
            self.assertEqual(w.location.range_.start.column, 9)
            self.assertEqual(w.location.range_.end.line, 10)
            self.assertEqual(w.location.range_.end.column, 44)

            self.assertEqual(w.location.point, None)

            # The line/column getters use the start:
            self.assertEqual(w.location.line, 10)
            self.assertEqual(w.location.column, 9)
예제 #39
0
def parse_splint_csv(path):
    """
    Parse a .csv file written by splint's "-csv FILENAME" option.
    Generate a list of Result instances.
    """
    generator = Generator(name='splint')
    metadata = Metadata(generator, None, None, None)
    analysis = Analysis(metadata, [])
    with open(path, 'r') as f:
        reader = csv.reader(f)
        for raw_row in reader:
            # Skip the initial title row
            if raw_row[0] == 'Warning':
                continue
            rowobj = parse_row(raw_row)
            analysis.results.append(rowobj.to_issue())

    return analysis
예제 #40
0
class FindbugsTestCase(unittest.TestCase):
    filepath = 'tests/resources/libjdom1-java_1.1.3-1_all.deb'
    firehose_results = Analysis(
        metadata=Metadata(
            generator=Generator(
                name='findbugs'
                ),
            sut=None,
            file_=None,
            stats=None),
        results=[]
        )


    @mock.patch('debile.slave.runners.findbugs.run_command',
            return_value=('2.0.3', '', 0))
    def test_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'findbugs')
        self.assertEquals(ver, '2.0.3')


    @mock.patch('debile.slave.runners.findbugs.run_command',
            return_value=('2.0.3', '', 1))
    def test_version_without_findbugs(self, mock):
        self.assertRaises(Exception, version)


    def test_findbugs(self):
        findbugs_analysis = findbugs(self.filepath, self.firehose_results)
        content = findbugs_analysis[1]
        self.assertTrue("The following classes needed for analysis" in content)

        # It think it is safe to say that the string is not 4 chars long
        self.assertTrue(len(content) > 4)


    @mock.patch('debile.slave.runners.findbugs.run_command',
            return_value=(0, 'error', -1))
    def test_findbugs_with_exception(self, mock):
        self.assertRaises(Exception, findbugs, self.filepath,
            self.firehose_results)
예제 #41
0
 def make_simple_analysis(self):
     """
     Construct a minimal Analysis instance
     """
     a = Analysis(
         metadata=Metadata(generator=Generator(name='cpychecker'),
                           sut=None,
                           file_=None,
                           stats=None),
         results=[
             Issue(cwe=None,
                   testid=None,
                   location=Location(file=File('foo.c', None),
                                     function=None,
                                     point=Point(10, 15)),
                   message=Message(text='something bad involving pointers'),
                   notes=None,
                   trace=None)
         ])
     return a, a.results[0]
예제 #42
0
    def test_example_2(self):
        # Verify that the parser works:
        with open('examples/example-2.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')
            self.assertEqual(a.metadata.generator.version, '0.11')
            self.assertIsInstance(a.metadata.sut, SourceRpm)
            self.assertEqual(a.metadata.sut.name, 'python-ethtool')
            self.assertEqual(a.metadata.sut.version, '0.7')
            self.assertEqual(a.metadata.sut.release, '4.fc19')
            self.assertEqual(a.metadata.sut.buildarch, 'x86_64')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Issue)
            self.assertEqual(w.cwe, 401)
            self.assertEqual(w.testid, 'refcount-too-high')
            self.assertEqual(w.location.file.givenpath, 'examples/python-src-example.c')
            self.assertEqual(w.location.file.abspath, None)
            self.assertEqual(w.location.file.hash_.alg, 'sha1')
            self.assertEqual(w.location.file.hash_.hexdigest,
                             '6ba29daa94d64b48071e299a79f2a00dcd99eeb1')
            self.assertEqual(w.location.function.name, 'make_a_list_of_random_ints_badly')
            self.assertEqual(w.location.line, 40)
            self.assertEqual(w.location.column, 4)
            self.assertEqual(w.message.text, "ob_refcnt of '*item' is 1 too high")
            self.assertMultiLineEqual(w.notes.text,
                ("was expecting final item->ob_refcnt to be N + 1 (for some unknown N)\n"
                 "due to object being referenced by: PyListObject.ob_item[0]\n"
                 "but final item->ob_refcnt is N + 2"))

            self.assertIsInstance(w.trace, Trace)
            self.assertEqual(len(w.trace.states), 3)
            s0 = w.trace.states[0]
            self.assertIsInstance(s0, State)
            self.assertEqual(s0.location.file.givenpath, 'examples/python-src-example.c')
            self.assertEqual(s0.location.function.name, 'make_a_list_of_random_ints_badly')
            self.assertEqual(s0.location.line, 36)
            self.assertEqual(s0.location.column, 14)
            self.assertEqual(s0.notes.text,
                'PyLongObject allocated at:         item = PyLong_FromLong(random());')
예제 #43
0
    def test_json_roundtrip(self):
        verbose = False

        def roundtrip_through_json(a):
            jsondict = a.to_json()
            if verbose:
                from pprint import pprint
                pprint(jsondict)
            return Analysis.from_json(jsondict)

        a1, w = self.make_simple_analysis()
        a2 = roundtrip_through_json(a1)

        self.assertEqual(a1.metadata, a2.metadata)
        self.assertEqual(a1.results, a2.results)
        self.assertEqual(a1, a2)

        a3, w = self.make_complex_analysis()
        a4 = roundtrip_through_json(a3)

        self.assertEqual(a3.metadata, a4.metadata)
        self.assertEqual(a3.results, a4.results)
        self.assertEqual(a3, a4)

        a5, f = self.make_failed_analysis()
        a6 = roundtrip_through_json(a5)

        self.assertEqual(a5.metadata, a6.metadata)
        self.assertEqual(a5.results, a6.results)
        self.assertEqual(a5, a6)

        a7, info = self.make_info()
        a8 = roundtrip_through_json(a7)

        self.assertEqual(a7.metadata, a8.metadata)
        self.assertEqual(a7.results, a8.results)
        self.assertEqual(a7, a8)

        a9 = Analysis.from_xml('examples/example-non-ascii.xml')
        a10 = roundtrip_through_json(a9)
        self.assertEqual(a9, a10)
예제 #44
0
    def test_example_4(self):
        with open('examples/example-4.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, 'cpychecker')
            self.assertEqual(a.metadata.generator.version, '0.11')
            self.assertIsInstance(a.metadata.sut, SourceRpm)
            self.assertEqual(a.metadata.sut.name, 'python-ethtool')
            self.assertEqual(a.metadata.sut.version, '0.7')
            self.assertEqual(a.metadata.sut.release, '4.fc19')
            self.assertEqual(a.metadata.sut.buildarch, 'x86_64')

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Failure)
            self.assertEqual(w.failureid, 'python-exception')
            self.assertEqual(w.location.file.givenpath, 'wspy_register.c')
            self.assertEqual(w.location.function.name,
                             'register_all_py_protocols_func')
            self.assertEqual(w.location.line, 159)
            self.assertEqual(w.location.column, 42)
            self.assert_(w.customfields['traceback'].startswith('wspy_register.c: In function \'register_all_py_protocols_func\':\n'))
예제 #45
0
    def test_non_ascii_example(self):
        with open('examples/example-non-ascii.xml') as f:
            a = Analysis.from_xml(f)
            self.assertEqual(a.metadata.generator.name, u('\u2620') * 8)

            self.assertEqual(len(a.results), 1)
            w = a.results[0]
            self.assertIsInstance(w, Issue)

            # Verify the Japanese version of
            #  "comparison between signed and unsigned integer expressions"
            # within the message:
            self.assertEqual(w.message.text,
                             (u('\u7b26\u53f7\u4ed8\u304d\u3068\u7b26\u53f7'
                                '\u7121\u3057\u306e\u6574\u6570\u5f0f\u306e'
                                '\u9593\u3067\u306e\u6bd4\u8f03\u3067\u3059')))

            # Verify the "mojibake" Kanji/Hiragana within the notes:
            self.assertIn(u('\u6587\u5b57\u5316\u3051'),
                          w.notes.text)

            self.assertEqual(w.location.function.name, u('oo\u025f'))
예제 #46
0
    def test_xml_roundtrip(self):
        def roundtrip_through_xml(a):
            xmlbytes = a.to_xml_bytes()

            buf = BytesIO(xmlbytes)
            return Analysis.from_xml(buf)

        a1, w = self.make_simple_analysis()
        a2 = roundtrip_through_xml(a1)

        self.assertEqual(a1.metadata, a2.metadata)
        self.assertEqual(a1.results, a2.results)
        self.assertEqual(a1, a2)

        a3, w = self.make_complex_analysis()
        a4 = roundtrip_through_xml(a3)

        self.assertEqual(a3.metadata, a4.metadata)
        self.assertEqual(a3.results, a4.results)
        self.assertEqual(a3, a4)

        a5, f = self.make_failed_analysis()
        a6 = roundtrip_through_xml(a5)

        self.assertEqual(a5.metadata, a6.metadata)
        self.assertEqual(a5.results, a6.results)
        self.assertEqual(a5, a6)

        a7, info = self.make_info()
        a8 = roundtrip_through_xml(a7)

        self.assertEqual(a7.metadata, a8.metadata)
        self.assertEqual(a7.results, a8.results)
        self.assertEqual(a7, a8)

        a9 = Analysis.from_xml('examples/example-non-ascii.xml')
        a10 = roundtrip_through_xml(a9)
        self.assertEqual(a9, a10)
예제 #47
0
    def test_json_roundtrip(self):
        def roundtrip_through_json(a):
            jsondict = a.to_json()
            from pprint import pprint
            pprint(jsondict)
            return Analysis.from_json(jsondict)

        a1, w = self.make_simple_analysis()
        a2 = roundtrip_through_json(a1)

        self.assertEqual(a1.metadata, a2.metadata)
        self.assertEqual(a1.results, a2.results)
        self.assertEqual(a1, a2)

        a3, w = self.make_complex_analysis()
        a4 = roundtrip_through_json(a3)

        self.assertEqual(a3.metadata, a4.metadata)
        self.assertEqual(a3.results, a4.results)
        self.assertEqual(a3, a4)

        a5, f = self.make_failed_analysis()
        a6 = roundtrip_through_json(a5)

        self.assertEqual(a5.metadata, a6.metadata)
        self.assertEqual(a5.results, a6.results)
        self.assertEqual(a5, a6)

        a7, info = self.make_info()
        a8 = roundtrip_through_json(a7)

        self.assertEqual(a7.metadata, a8.metadata)
        self.assertEqual(a7.results, a8.results)
        self.assertEqual(a7, a8)

        a9 = Analysis.from_xml('examples/example-non-ascii.xml')
        a10 = roundtrip_through_json(a9)
        self.assertEqual(a9, a10)
예제 #48
0
        def roundtrip_through_xml(a):
            xmlbytes = a.to_xml_bytes()

            buf = BytesIO(xmlbytes)
            return Analysis.from_xml(buf)
예제 #49
0
 def roundtrip_through_json(a):
     jsondict = a.to_json()
     from pprint import pprint
     pprint(jsondict)
     return Analysis.from_json(jsondict)
예제 #50
0
 def parse_xml_bytes(self, xmlbytes):
     f = BytesIO(xmlbytes)
     a = Analysis.from_xml(f)
     f.close()
     return a
def get_analyses(mockdir):
    analyses = []
    for filename in glob.glob(os.path.join(mockdir, 'reports', '*.xml')):
        r = Analysis.from_xml(filename)
        analyses.append( (filename, r) )
    return analyses
 def get_analyses(self):
     analyses = []
     for filename in glob.glob(os.path.join(self.get_reports_dir(), '*.xml')):
         r = Analysis.from_xml(filename)
         analyses.append( (filename, r) )
     return analyses