示例#1
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
示例#2
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')
示例#3
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
示例#4
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
示例#5
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)
示例#6
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)
示例#7
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)
示例#8
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
示例#9
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)
示例#10
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=[])
示例#11
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]
示例#12
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
示例#13
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)
示例#14
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]
示例#15
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
示例#16
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]
示例#17
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)
示例#18
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]
示例#19
0
def parse_file(data_file, sut=None, file_=None, stats=None):
    """
    for each line, the regex for SPARECODE_WARNING is matched

    :param data_file:   file object containing build log
    :type  data_file:   file

    :return:    Analysis instance
    """

    generator = Generator(name='frama-c')
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])

    for line in data_file.readlines():
        match_warning = FRAMA_C_SPARECODE_PATTERN.match(line)

        if match_warning:
            issue = parse_warning(match_warning)
            analysis.results.append(issue)
    return analysis
示例#20
0
def parse_file(data_file, gccversion=None, sut=None, file_=None, stats=None):
    """
    looks for groups of lines that start with a line identifying a function
    name, followed by one or more lines with a warning or note

    :param data_file:   file object containing build log
    :type  data_file:   file
    :param gccversion:   version of GCC that generated this report
    :type  gccversion:   str

    :return:    Analysis instance
    """
    # has a value only when in a block of lines where the first line identifies
    # a function and is followed by 0 or more warning lines

    generator = Generator(name='gcc', version=gccversion)
    metadata = Metadata(generator, sut, file_, stats)
    analysis = Analysis(metadata, [])

    current_func_name = None
    for line in data_file.readlines():
        match_func = FUNCTION_PATTERN.match(line)
        match_global = GLOBAL_PATTERN.match(line)
        # if we found a line that describes a function name
        if match_func:
            current_func_name = match_func.group('func')
        elif match_global:
            current_func_name = GLOBAL_FUNC_NAME

        # if we think the next line might describe a warning
        elif current_func_name is not None:
            issue = parse_warning(line, current_func_name)
            if issue:
                analysis.results.append(issue)
            else:
                # reset this when we run out of warnings associated with it
                current_func_name = None
    return analysis
示例#21
0
def test_lintian_common():
    firehorse_results = Analysis(metadata=Metadata(
        generator=Generator(name='lintian'), sut=None, file_=None, stats=None),
                                 results=[])

    return lintian(filepath, firehorse_results)
示例#22
0
def parse_file(infile):
    """ Parser flawfinder output

    :infile: file-like object
    :returns: Firehose Analysis object, representing the final XML.

    Flawfinder can generate multiple cwes for a single issue.
    Firehose's models does not supports multiple CWEs.
    For now, when  multiple CWEs ocurrs, we get only the first one.

    A issue was created to track this bug:
    https://github.com/fedora-static-analysis/firehose/issues/35
    """

    line = infile.readline()
    generator = Generator(name='flawfinder',
                          version=get_flawfinder_version(line))
    metadata = Metadata(generator, None, None, None)
    analysis = Analysis(metadata, [])

    # A regex for "filename:linenum:"
    ISSUE_LINE_PATTERN = r"(\S.*)\:([0-9]+)\:"

    # A regex for the reported severity, e.g. "[2]"
    ISSUE_SEVERITY_PATTERN = r"\[([0-9]+)\]"

    # A regex for the reported testid, e.g. "(buffer)"
    ISSUE_TESTID_PATTERN = r"\(([a-z]+)\)"

    WHITESPACE = "\s+"

    FIRST_LINE_PATTERN = (ISSUE_LINE_PATTERN + WHITESPACE +
                     ISSUE_SEVERITY_PATTERN + WHITESPACE +
                     ISSUE_TESTID_PATTERN)
    prog = re.compile(FIRST_LINE_PATTERN)
    while line:
        m = prog.match(line)
        if m:
            issue_path = m.group(1)
            issue_line = m.group(2)
            issue_severity = m.group(3)
            testid = m.group(4)

            location = Location(file=File(issue_path, None),
                                function=None,
                                point=Point(int(issue_line), 0))

            message_line = infile.readline()
            issue_message = ""
            while not prog.search(message_line) and message_line != "\n":
                # Build up issue_message as one line, stripping out
                # extraneous whitespace.
                if issue_message:
                    issue_message += " " + message_line.strip()
                else:
                    issue_message = message_line.strip()
                message_line = infile.readline()

            line = message_line

            cwes = [int(cwe) for cwe in re.findall("CWE-([0-9]+)",
                                                   issue_message)]
            if cwes:
                first_cwe = int(cwes[0])
            else:
                first_cwe = None

            issue = Issue(first_cwe, testid, location,
                          Message(text=issue_message), notes=None,
                          trace=None, severity=issue_severity, customfields=None)

            analysis.results.append(issue)
        else:
            line = infile.readline()

    return analysis
示例#23
0
def test_roodi_common():
    firehorse_results = Analysis(metadata=Metadata(
        generator=Generator(name='roodi'), sut=None, file_=None, stats=None),
                                 results=[])

    return roodi("tests/resources/bundler_1.7.4-1.dsc", firehorse_results)
示例#24
0
class JSHintTestCase(unittest.TestCase):
    filepath = "tests/resources/libjs-term.js_0.0.4-1.dsc"
    firehose_results = Analysis(
        metadata=Metadata(
            generator=Generator(
                name='jshint'
                ),
            sut=None,
            file_=None,
            stats=None),
        results=[]
        )


    @mock.patch('debile.slave.runners.jshint.run_command',
            return_value=('output', 'jshint v2.8.0', 0))
    def test_jshint_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'jshint')
        self.assertEquals(ver, 'v2.8.0')


    @mock.patch('debile.slave.runners.jshint.run_command',
            return_value=('output', 'jshint v2.8.0', 1))
    def test_version_raise_exception(self, mock):
        self.assertRaises(Exception, version)


    def test_jshint(self):
        jshint_analysis = jshint(self.filepath, self.firehose_results)
        content = jshint_analysis[1]

        self.assertTrue("Bad line breaking" 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.jshint.run_command',
            return_value=(None, 'jshint v2.8.0', 1))
    def test_jshint_with_none_output(self, mock):
        jshint_analysis = jshint(self.filepath, self.firehose_results)

        self.assertEquals(jshint_analysis[0], self.firehose_results)
        self.assertIsNone(jshint_analysis[1])
        self.assertTrue(jshint_analysis[2])
        self.assertIsNone(jshint_analysis[3])
        self.assertIsNone(jshint_analysis[4])


    def test_jshint_wrappers(self):
        jshint_analysis = jshint(self.filepath, self.firehose_results)
        issues = parse_jshint(jshint_analysis[1].splitlines())
        i = 0
        found = None
        for issue in issues:
            if issue.location.file.givenpath == "test/index.js" and \
            issue.location.point.line==13 and issue.location.point.column==19:
                found = issue
            i += 1
        print found
        self.assertEquals(found.testid, "W014")
        self.assertEquals(found.location.file.givenpath, "test/index.js")
        self.assertEquals(found.location.point.line, 13)
        self.assertEquals(found.location.point.column, 19)
        self.assertEquals(found.severity, "warning")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue("Bad line breaking" in found.message.text)
        self.assertTrue(i > 75)
示例#25
0
class CppcheckTestCase(unittest.TestCase):
    file_path="tests/resources/libjsoncpp_0.6.0~rc2-3.1.dsc"
    firehose_results = Analysis(
        metadata=Metadata(
            generator=Generator(
                name='cppcheck'
                ),
            sut=None,
            file_=None,
            stats=None),
        results=[]
        )


    @mock.patch('debile.slave.runners.cppcheck.run_command',
            return_value=('Cppcheck 1.69', '', 0))
    def test_cppcheck_version(self, mock):
        name, ver = version()

        self.assertEquals(name, 'Cppcheck')
        self.assertEquals(ver, '1.69')


    @mock.patch('debile.slave.runners.cppcheck.run_command',
            return_value=('Cppcheck 1.69', '', 1))
    def test_cppcheck_version_with_error(self, mock):
        self.assertRaises(Exception, version)


    def test_cppcheck(self):
        cpp_analysis = cppcheck(self.file_path, self.firehose_results)
        xml_content = cpp_analysis[1]
        tree = lxml.etree.fromstring(xml_content.encode('utf-16'))
        i = 0
        paths = []
        lines = []
        severity = []
        messages = []
        testid = []
        for result in tree.xpath("//results/error"):
            paths.append(result.attrib['file'])
            lines.append(result.attrib['line'])
            severity.append(result.attrib['severity'])
            messages.append(result.attrib['msg'])
            testid.append(result.attrib['id'])
            i += 1
        # It think it is safe to say that this number won't be less than 4
        self.assertTrue(i > 4)
        # Check that some values exist (the order might change)
        self.assertTrue("src/lib_json/json_value.cpp" in paths)
        self.assertTrue("style" in severity)
        self.assertTrue("704" in lines)
        self.assertTrue("toomanyconfigs" in testid)


    @mock.patch('debile.slave.runners.cppcheck.run_command',
            return_value=('', '    ', 0))
    def test_cppcheck_with_withespace_in_stderr(self, mock):
        cpp_analysis = cppcheck(self.file_path, self.firehose_results)

        self.assertEquals(cpp_analysis[0], self.firehose_results)
        self.assertEquals(cpp_analysis[1], '    ')
        self.assertFalse(cpp_analysis[2])
        self.assertIsNone(cpp_analysis[3])
        self.assertIsNone(cpp_analysis[4])


    def test_cppcheck_wrappers(self):
        cpp_analysis = cppcheck(self.file_path, self.firehose_results)
        issues = parse_cppcheck(cpp_analysis[1])
        i = 0
        for issue in issues:
            if issue.testid == "toomanyconfigs":
                found = issue
            i += 1
        self.assertEquals(found.testid, "toomanyconfigs")
        self.assertEquals(found.location.file.givenpath,
                "src/lib_json/json_value.cpp")
        self.assertEquals(found.location.point.line, 0)
        self.assertEquals(found.location.point.column, 0)
        self.assertEquals(found.severity, "style")
        self.assertIsNone(found.notes)
        self.assertIsNone(found.customfields)
        self.assertTrue(i > 4)
示例#26
0
def test_pep8_common():
    firehorse_results = Analysis(metadata=Metadata(
        generator=Generator(name='pep8'), sut=None, file_=None, stats=None),
                                 results=[])

    return pep8(filepath, firehorse_results)
示例#27
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, [])

    if 'clang_version' in plist:
        generator.version = plist['clang_version']

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

        cwe = None

        customfields = CustomFields()
        for key in ['category', 'issue_context', 'issue_context_kind']:
            if key in diagnostic:
                customfields[key] = diagnostic[key]

        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,
            # Use the 'type' field for the testid:
            diagnostic['type'],
            location,
            message,
            notes,
            trace,
            customfields=customfields)

        analysis.results.append(issue)

    return analysis