File: flawfinder.py

package info (click to toggle)
python-firehose 0.5-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,104 kB
  • sloc: python: 2,587; xml: 1,388; makefile: 152; ansic: 34
file content (132 lines) | stat: -rw-r--r-- 4,408 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
#
#   Copyright 2017 David Carlos  <ddavidcarlos1392@gmail.com>
#   This library is free software; you can redistribute it and/or
#   modify it under the terms of the GNU Lesser General Public
#   License as published by the Free Software Foundation; either
#   version 2.1 of the License, or (at your option) any later version.
#
#   This library is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public
#   License along with this library; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#   USA

import sys
import re
from subprocess import check_output
from firehose.model import Message, Point, \
    File, Location, Generator, Metadata, Analysis, Issue


def main():
    """ Main entry to flawfinder parser """
    try:
        arg_file = sys.argv[1]
        report = open(arg_file, 'r')
    except IOError as e:
        raise e
    except IndexError:
        print("Missing input file")
    analysis = parse_file(report)
    sys.stdout.write(str(analysis.to_xml()))
    sys.stdout.write('\n')


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


def get_flawfinder_version(first_line):
    """Retrieve flawfinder version from report.
    :first_line: first line of the flawfinder report.
    :returns: flawfinder version.
    """

    pattern = "version\s([0-9]?.[0-9]*)"
    prog = re.compile(pattern)
    try:
        return prog.search(first_line).groups()[0]
    except IndexError:
        return None

if __name__ == '__main__':
    main()