-
Notifications
You must be signed in to change notification settings - Fork 2
/
create_report.py
227 lines (179 loc) · 9.05 KB
/
create_report.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
'''
Copyright 2020 Flexera Software LLC
See LICENSE.TXT for full license text
SPDX-License-Identifier: MIT
Author : sgeary
Created On : Fri Aug 07 2020
File : create_report.py
'''
import sys, os, logging, argparse, json, re
from datetime import datetime
import _version
import report_data
import report_artifacts
import report_errors
import common.api.project.upload_reports
import common.api.system.release
import common.report_archive
###################################################################################
# Test the version of python to make sure it's at least the version the script
# was tested on, otherwise there could be unexpected results
if sys.version_info < (3, 6):
raise Exception("The current version of Python is less than 3.6 which is unsupported.\n Script created/tested against python version 3.6.8. ")
else:
pass
propertiesFile = "../server_properties.json" # Created by installer or manually
propertiesFile = os.path.dirname(os.path.realpath(__file__)) + "/" + propertiesFile
logfileName = os.path.dirname(os.path.realpath(__file__)) + "/_project_inventory_report.log"
###################################################################################
# Set up logging handler to allow for different levels of logging to be capture
logging.basicConfig(format='%(asctime)s,%(msecs)-3d %(levelname)-8s [%(filename)-30s:%(lineno)-4d] %(message)s', datefmt='%Y-%m-%d:%H:%M:%S', filename=logfileName, filemode='w',level=logging.DEBUG)
logger = logging.getLogger(__name__)
logging.getLogger("urllib3").setLevel(logging.WARNING) # Disable logging for requests module
####################################################################################
# Create command line argument options
parser = argparse.ArgumentParser()
parser.add_argument('-pid', "--projectID", help="Project ID")
parser.add_argument("-rid", "--reportID", help="Report ID")
parser.add_argument("-authToken", "--authToken", help="Code Insight Authorization Token")
parser.add_argument("-reportOpts", "--reportOptions", help="Options for report content")
#----------------------------------------------------------------------#
def main():
reportName = "Project Inventory Report"
reportVersion = _version.__version__
logger.info("Creating %s - %s" %(reportName, reportVersion))
print("Creating %s - %s" %(reportName, reportVersion))
print(" Logfile: %s" %(logfileName))
#####################################################################################################
# Code Insight System Information
# Pull the base URL from the same file that the installer is creating
if os.path.exists(propertiesFile):
try:
file_ptr = open(propertiesFile, "r")
configData = json.load(file_ptr)
baseURL = configData["core.server.url"]
file_ptr.close()
logger.info("Using baseURL from properties file: %s" %propertiesFile)
except:
logger.error("Unable to open properties file: %s" %propertiesFile)
# Is there a self signed certificate to consider?
try:
certificatePath = configData["core.server.certificate"]
os.environ["REQUESTS_CA_BUNDLE"] = certificatePath
os.environ["SSL_CERT_FILE"] = certificatePath
logger.info("Self signed certificate added to env")
except:
logger.info("No self signed certificate in properties file")
else:
baseURL = "http://localhost:8888" # Required if the core.server.properties files is not used
logger.info("Using baseURL from create_report.py")
# See what if any arguments were provided
args = parser.parse_args()
projectID = args.projectID
reportID = args.reportID
authToken = args.authToken
reportOptions = args.reportOptions
fileNameTimeStamp = datetime.now().strftime("%Y%m%d-%H%M%S")
reportTimeStamp = datetime.strptime(fileNameTimeStamp, "%Y%m%d-%H%M%S").strftime("%B %d, %Y at %H:%M:%S")
# Based on how the shell pass the arguemnts clean up the options if on a linux system:w
if sys.platform.startswith('linux'):
reportOptions = reportOptions.replace('""', '"')[1:-1]
reportOptions = json.loads(reportOptions)
reportOptions = verifyOptions(reportOptions)
releaseDetails = common.api.system.release.get_release_details(baseURL, authToken)
releaseVersion = releaseDetails["fnci.release.name"].replace(" ", "")
logger.debug("Code Insight Release: %s" %releaseVersion)
logger.debug("Custom Report Provided Arguments:")
logger.debug(" projectID: %s" %projectID)
logger.debug(" reportID: %s" %reportID)
logger.debug(" baseURL: %s" %baseURL)
logger.debug(" reportOptions: %s" %reportOptions)
reportData = {}
reportData["projectID"] = projectID
reportData["reportName"] = reportName
reportData["reportVersion"] = reportVersion
reportData["reportOptions"] = reportOptions
reportData["releaseVersion"] = releaseVersion
reportData["fileNameTimeStamp"] = fileNameTimeStamp
reportData["reportTimeStamp"] = reportTimeStamp
# Did we fail the options validation?
if "errorMsg" in reportOptions.keys():
reportFileNameBase = reportName.replace(" ", "_") + "-Creation_Error-" + fileNameTimeStamp
reportData["errorMsg"] = reportOptions["errorMsg"]
reportData["reportName"] = reportName
reportData["reportFileNameBase"] = reportFileNameBase
reports = report_errors.create_error_report(reportData)
print(" *** ERROR *** Error found validating report options")
else:
print(" Collect data for %s" %reportName)
reportData = report_data.gather_data_for_report(baseURL, projectID, authToken, reportData)
print(" Report data has been collected")
projectName = reportData["topLevelProjectName"]
projectNameForFile = re.sub(r"[^a-zA-Z0-9]+", '-', projectName ) # Remove special characters from project name for artifacts
# Are there child projects involved? If so have the artifact file names reflect this fact
if len(reportData["projectList"])==1:
reportFileNameBase = projectNameForFile + "-" + str(projectID) + "-" + reportName.replace(" ", "_") + "-" + fileNameTimeStamp
else:
reportFileNameBase = projectNameForFile + "-with-children-" + str(projectID) + "-" + reportName.replace(" ", "_") + "-" + fileNameTimeStamp
reportData["reportFileNameBase"] = reportFileNameBase
if "errorMsg" in reportData.keys():
reports = report_errors.create_error_report(reportData)
print(" Error report artifacts have been created")
else:
reports = report_artifacts.create_report_artifacts(reportData)
print(" Report artifacts have been created")
print(" Create report archive for upload")
uploadZipfile = common.report_archive.create_report_zipfile(reports, reportFileNameBase)
print(" Upload zip file creation completed")
common.api.project.upload_reports.upload_project_report_data(baseURL, projectID, reportID, authToken, uploadZipfile)
print(" Report uploaded to Code Insight")
#########################################################
# Remove the file since it has been uploaded to Code Insight
try:
os.remove(uploadZipfile)
except OSError:
logger.error("Error removing %s" %uploadZipfile)
print("Error removing %s" %uploadZipfile)
logger.info("Completed creating %s" %reportName)
print("Completed creating %s" %reportName)
#----------------------------------------------------------------------#
def verifyOptions(reportOptions):
'''
Expected Options for report:
includeChildProjects - True/False
'''
reportOptions["errorMsg"] = []
trueOptions = ["true", "t", "yes", "y"]
falseOptions = ["false", "f", "no", "n"]
includeChildProjects = reportOptions["includeChildProjects"]
includeComplianceInformation = reportOptions["includeComplianceInformation"]
maxVersionsBack = reportOptions["maxVersionsBack"]
cvssVersion = reportOptions["cvssVersion"]
if includeChildProjects.lower() in trueOptions:
reportOptions["includeChildProjects"] = "true"
elif includeChildProjects.lower() in falseOptions:
reportOptions["includeChildProjects"] = "false"
else:
reportOptions["errorMsg"].append("Invalid option for including child projects: <b>%s</b>. Valid options are <b>True/False</b>" %includeChildProjects)
if includeComplianceInformation.lower() in trueOptions:
reportOptions["includeComplianceInformation"] = True
elif includeComplianceInformation.lower() in falseOptions:
reportOptions["includeComplianceInformation"] = False
else:
reportOptions["errorMsg"].append("Invalid option for including compliance information: <b>%s</b>. Valid options are <b>True/False</b>" %includeComplianceInformation)
if maxVersionsBack.isdigit ():
reportOptions["maxVersionsBack"] = maxVersionsBack
else:
reportOptions["errorMsg"].append("Invalid value for the maximun number of versions from the most recent: <b>%s</b>. An postive interger number is required" %maxVersionsBack)
if cvssVersion.startswith("2"):
reportOptions["cvssVersion"] = "2.0"
elif cvssVersion.startswith("3"):
reportOptions["cvssVersion"] = "3.x"
else:
reportOptions["errorMsg"].append("Invalid option for CVSS Version: <b>%s</b>. Valid options are <b>2.0/3.x</b>" %cvssVersion)
if not reportOptions["errorMsg"]:
reportOptions.pop('errorMsg', None)
return reportOptions
#----------------------------------------------------------------------#
if __name__ == "__main__":
main()