forked from jaxley/python-fortify
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathproject.py
144 lines (113 loc) · 5.72 KB
/
project.py
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
133
134
135
136
137
138
139
140
141
142
143
144
from __future__ import print_function
from . import FPR, Issue, RemovedIssue
import sys
import logging
logger = logging.getLogger(__name__)
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
# configures fortify project objects
class ProjectFactory:
# creates a new project object by loading the FPR from fprpath and building necessary data structures
def __init__(self):
pass
@staticmethod
def create_project(fprpath):
fpr = FPR(fprpath)
project = Project(fpr)
# find every vulnerability and model as an Issue object attached to the project
logger.debug("Getting Vulnerabilities from FVDL")
for vuln in fpr.FVDL.get_vulnerabilities():
issue = Issue.from_vulnerability(vuln)
rule = fpr.FVDL.EngineData.RuleInfo.get_rule(vuln.ClassInfo.ClassID)
if rule is not None and hasattr(rule, 'metadata'):
issue.add_metadata(rule.metadata)
# now, we need to apply visibility rules from the filtertemplate, if one exists, for the
if fpr.FilterTemplate is not None:
issue.hidden = fpr.FilterTemplate.is_hidden(fpr, issue)
project.add_or_update_issue(issue)
# now, associate the analysis info with the issues we know about.
# Only FPRs with audit information will have this to associate.
logger.debug("Getting Issues for project and setting suppressed and analysis data.")
issues = project.get_issues()
logger.debug("Have to process %d issues." % len(issues))
# build lookup
fpr.Audit.build_issue_analysis_lookup()
for issueid in issues:
i = project.get_issue(issueid)
analysisInfo = fpr.Audit.get_issue_analysis(issueid)
if analysisInfo is not None:
# set suppressed status
i.suppressed = analysisInfo['suppressed']
if analysisInfo['analysis'] is not None:
i.analysis = analysisInfo['analysis']
project.add_or_update_issue(i) # add it back in to replace the previous one
# now, add information about removed issues
logger.debug("Getting information about removed issues")
if hasattr(fpr.Audit, 'IssueList') and hasattr(fpr.Audit.IssueList, 'RemovedIssue'):
for removed in fpr.Audit.IssueList.RemovedIssue:
ri = RemovedIssue.from_auditxml(removed)
project.add_or_update_issue(ri)
removedissues = [i for i in list(issues.values()) if i.removed]
suppressedissues = [i for i in list(issues.values()) if i.suppressed]
hiddenissues = [i for i in list(issues.values()) if i.hidden]
naiissues = [i for i in list(issues.values()) if i.is_NAI()]
eprint("Got [%d] issues, [%d] hidden, [%d] NAI, [%d] Suppressed, [%d] Removed" % (len(issues), len(hiddenissues), len(naiissues), len(suppressedissues), len(removedissues)))
return project # A fortify project, containing one or more issues, with metadata
class Project:
def __init__(self, fpr):
self._fpr = fpr
self._issues = {}
# set project properties
if hasattr(fpr.Audit.ProjectInfo, 'Name'):
self.ProjectName=fpr.Audit.ProjectInfo.Name
else:
self.ProjectName=None
if hasattr(fpr.Audit.ProjectInfo, 'ProjectVersionId'):
self.ProjectVersionId=fpr.Audit.ProjectInfo.ProjectVersionId
else:
self.ProjectVersionId=None
for loc in fpr.FVDL.Build.LOC:
if loc.attrib['type'] == 'Fortify':
self.ScannedELOC=loc.text
elif loc.attrib['type'] == 'Line Count':
self.ScannedLOC=loc.text
def add_or_update_issue(self, issue):
if issue.id in self._issues:
# remove first and decrement counts, if change in severity
current = self._issues[issue.id]
if issue != current:
# unless this is a new object, nothing to do
del self._issues[issue.id]
# add the issue to the list, if necessary
self._issues[issue.id] = issue
def get_issues(self):
return self._issues
def get_issue(self, id):
return self._issues[id]
def print_project_info(self):
# TODO: print an overview of the project information (name, etc.) and scan information
return
def print_vuln_counts(self):
vuln_counts = {'Critical': 0,
'High': 0,
'Medium': 0,
'Low': 0,
}
for i in list(self._issues.values()):
# exclude hidden, NAI and suppressed (TODO: could be configurable)
if not (i.hidden or i.is_NAI() or i.suppressed):
if i.risk is None:
logger.warn("Risk calculation error for issue [%s]" % i.id)
else:
vuln_counts[i.risk] += 1
print("Critical, High, Medium, Low")
print("%d, %d, %d, %d" % (vuln_counts['Critical'], vuln_counts['High'], vuln_counts['Medium'], vuln_counts['Low']))
def print_vuln_summaries(self, open_high_priority):
# TODO: enable sorting by severity and file_line by default.
print("file_line,path,id,kingdom,type_subtype,severity,nai,filtered,suppressed,removed,analysis")
for i in self._issues.values():
if not open_high_priority or i.is_open_high_priority:
print("%s:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" % \
(i.metadata['shortfile'], i.metadata['line'], i.metadata['file'], i.id, i.kingdom, i.category, i.risk, i.is_NAI(), "H" if i.hidden else "V", i.suppressed, i.removed, i.analysis))
def get_fpr(self):
return self._fpr