Beispiel #1
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]
Beispiel #2
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)
        function = bugInstance.find("Method").find("Message").text
        tmpIndex = function.rfind("In method ") + len("In method ") - 1
        function = Function(function[tmpIndex + 1:])
        path = sourceLine.get("sourcepath")
        path = File(path, 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
Beispiel #3
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]
Beispiel #4
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
Beispiel #5
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)
Beispiel #6
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]
Beispiel #7
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
Beispiel #8
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
Beispiel #9
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)
Beispiel #10
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
Beispiel #11
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)
Beispiel #12
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
Beispiel #13
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)
Beispiel #14
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)
Beispiel #15
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)