blob: 5a02b1cf38b6eb117678dca1351a335d2fdb0e26 [file] [log] [blame]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +03001#!groovy
2
Ivan Udovichenko314a0732020-04-13 22:47:26 +03003package com.mirantis.mk
4
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +03005import groovy.json.JsonSlurper
Ivan Udovichenko6c337562020-08-06 16:22:26 +03006import groovy.json.JsonOutput
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +03007
8def callREST (String uri, String auth,
Ivan Udovichenko45252252020-04-14 22:45:53 +03009 String method = 'GET', String message = null) {
10 String authEnc = auth.bytes.encodeBase64()
11 def req = new URL(uri).openConnection()
12 req.setRequestMethod(method)
13 req.setRequestProperty('Content-Type', 'application/json')
14 req.setRequestProperty('Authorization', "Basic ${authEnc}")
15 if (message) {
16 req.setDoOutput(true)
17 req.getOutputStream().write(message.getBytes('UTF-8'))
18 }
19 Integer responseCode = req.getResponseCode()
20 String responseText = ''
21 if (responseCode == 200 || responseCode == 201) {
22 responseText = req.getInputStream().getText()
23 }
24 req = null
25 return [ 'responseCode': responseCode, 'responseText': responseText ]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +030026}
27
28def getTeam (String image = '') {
29 def team_assignee = ''
30 switch(image) {
31 case ~/^(tungsten|tungsten-operator)\/.*$/:
32 team_assignee = 'OpenContrail'
33 break
34 case ~/^bm\/.*$/:
35 team_assignee = 'BM/OS (KaaS BM)'
36 break
37 case ~/^openstack\/.*$/:
38 team_assignee = 'OpenStack hardening'
39 break
40 case ~/^stacklight\/.*$/:
41 team_assignee = 'Stacklight LMA'
42 break
43 case ~/^ceph\/.*$/:
44 team_assignee = 'Storage'
45 break
46 case ~/^iam\/.*$/:
47 team_assignee = 'KaaS'
48 break
49 case ~/^lcm\/.*$/:
50 team_assignee = 'Kubernetes'
51 break
52 default:
53 team_assignee = 'Release Engineering'
54 break
55 }
56
57 return team_assignee
58}
59
60def updateDictionary (String jira_issue_key, Map dict, String uri, String auth, String jira_user_id) {
61 def response = callREST("${uri}/${jira_issue_key}", auth)
62 if ( response['responseCode'] == 200 ) {
63 def issueJSON = new JsonSlurper().parseText(response["responseText"])
64 if (issueJSON.containsKey('fields')) {
65 if (!dict.containsKey(jira_issue_key)) {
66 dict[jira_issue_key] = [
67 summary : '',
68 description: '',
69 comments: []
70 ]
71 }
72 if (issueJSON['fields'].containsKey('summary')){
73 dict[jira_issue_key].summary = issueJSON['fields']['summary']
74 }
75 if (issueJSON['fields'].containsKey('description')) {
76 dict[jira_issue_key].description = issueJSON['fields']['description']
77 }
78 if (issueJSON['fields'].containsKey('comment') && issueJSON['fields']['comment']['comments']) {
79 issueJSON['fields']['comment']['comments'].each {
80 if (it.containsKey('author') && it['author'].containsKey('accountId') && it['author']['accountId'] == jira_user_id) {
81 dict[jira_issue_key]['comments'].add(it['body'])
82 }
83 }
84 }
85 }
86 }
87 return dict
88}
89
90def cacheLookUp(Map dict, String image_short_name, String image_full_name = '', String cve_id = '' ) {
91 def found_key = ['','']
92 if (!found_key[0] && dict && image_short_name) {
93 dict.each { issue_key_name ->
94 if (!found_key[0]) {
95 def s = dict[issue_key_name.key]['summary'] =~ /\b${image_short_name}\b/
96 if (s) {
97 if (image_full_name) {
98 def d = dict[issue_key_name.key]['description'] =~ /(?m)\b${image_full_name}\b/
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +030099 if (d) {
100 found_key = [issue_key_name.key,'']
101 } else {
102 if (dict[issue_key_name.key]['comments']) {
103 def comment_match = false
104 dict[issue_key_name.key]['comments'].each{ comment ->
105 if (!comment_match) {
106 def c = comment =~ /(?m)\b${image_full_name}\b/
107 if (c) {
108 comment_match = true
109 }
110 }
111 }
112 if (!comment_match) {
113 found_key = [issue_key_name.key,'na']
114 } else {
115 found_key = [issue_key_name.key,'']
116 }
117 } else {
118 found_key = [issue_key_name.key,'na']
119 }
120 }
121 }
122 }
123 }
124 }
125 }
126 return found_key
127}
128
Ivan Udovichenko6c337562020-08-06 16:22:26 +0300129def reportJiraTickets(String reportFileContents, String jiraCredentialsID, String jiraUserID, String jiraNamespace = 'PRODX') {
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300130
131 def dict = [:]
132
Ivan Udovichenko6c870b72020-04-14 11:31:51 +0300133 def common = new com.mirantis.mk.Common()
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300134 def cred = common.getCredentialsById(jiraCredentialsID)
135 def auth = "${cred.username}:${cred.password}"
136 def uri = "${cred.description}/rest/api/2/issue"
137
138 def search_api_url = "${cred.description}/rest/api/2/search"
139
140 def search_json = """
141{
Ivan Udovichenko897e32b2020-08-07 10:10:34 +0300142 "jql": "reporter = ${jiraUserID} and (labels = cve and labels = security) and (status = 'To Do' or status = 'For Triage' or status = Open or status = 'In Progress' or status = New)", "maxResults":-1
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300143}
144"""
145
146 def response = callREST("${search_api_url}", auth, 'POST', search_json)
147
148 def InputJSON = new JsonSlurper().parseText(response["responseText"])
149
150 InputJSON['issues'].each {
151 dict[it['key']] = [
152 summary : '',
153 description: '',
154 comments: []
155 ]
156 }
157
158 InputJSON['issues'].each { jira_issue ->
159 dict = updateDictionary(jira_issue['key'], dict, uri, auth, jiraUserID)
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300160 }
161
Ivan Udovichenko6c870b72020-04-14 11:31:51 +0300162 def reportJSON = new JsonSlurper().parseText(reportFileContents)
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300163 def imageDict = [:]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300164 reportJSON.each{
165 image ->
166 if ("${image.value}".contains('issues')) { return }
167 image.value.each{
168 pkg ->
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300169 pkg.value.each{
170 cve ->
171 if (cve[2] && (cve[1].contains('High') || cve[1].contains('Critical'))) {
Ivan Udovichenkoc774c6e2020-04-15 01:55:41 +0300172 if (!imageDict.containsKey(image.key)) {
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300173 imageDict.put(image.key, [:])
174 }
175 if (!imageDict[image.key].containsKey(pkg.key)) {
176 imageDict[image.key].put(pkg.key, [])
177 }
Ivan Udovichenkob2e52352020-06-29 18:03:56 +0300178 imageDict[image.key][pkg.key].add("[${cve[0]}|${cve[4]}] (${cve[2]}) (${cve[3]})")
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300179 }
180 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300181 }
182 }
183
184 def jira_summary = ''
185 def jira_description = ''
186 imageDict.each{
187 image ->
188 def image_key = image.key.replaceAll(/(^[a-z0-9-.]+.mirantis.(net|com)\/|:.*$)/, '')
Ivan Udovichenko6c337562020-08-06 16:22:26 +0300189 // Below change was produced due to other workflow for UCP Docker images (RE-274)
Ivan Udovichenko9721c312020-10-01 23:50:38 +0300190 if (image_key.startsWith('lcm/docker/ucp')) {
191 return
192 } else if (image_key.startsWith('mirantis/ucp')) {
Ivan Udovichenkofe3f11e2020-09-29 17:31:38 +0300193 jiraNamespace = 'ENGORC'
194 } else {
195 jiraNamespace = 'PRODX'
196 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300197 jira_summary = "[${image_key}] Found CVEs in Docker image"
Ivan Udovichenkoa87bcef2020-09-29 17:46:30 +0300198 jira_description = "${image.key}\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300199 image.value.each{
200 pkg ->
Ivan Udovichenkoa87bcef2020-09-29 17:46:30 +0300201 jira_description += "__* ${pkg.key}\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300202 pkg.value.each{
203 cve ->
Ivan Udovichenkoa87bcef2020-09-29 17:46:30 +0300204 jira_description += "________${cve}\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300205 }
206 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300207
208 def team_assignee = getTeam(image_key)
209
Ivan Udovichenkoadee8b52020-08-06 17:07:28 +0300210 def basicIssueJSON = new JsonSlurper().parseText('{"fields": {}}')
Ivan Udovichenko6c337562020-08-06 16:22:26 +0300211
Ivan Udovichenkoadee8b52020-08-06 17:07:28 +0300212 basicIssueJSON['fields'] = [
Ivan Udovichenkofe3f11e2020-09-29 17:31:38 +0300213 project:[
214 key:"${jiraNamespace}"
215 ],
Ivan Udovichenko6c337562020-08-06 16:22:26 +0300216 summary:"${jira_summary}",
217 description:"${jira_description}",
218 issuetype:[
Ivan Udovichenkofe3f11e2020-09-29 17:31:38 +0300219 name:'Bug'
Ivan Udovichenko6c337562020-08-06 16:22:26 +0300220 ],
221 labels:[
222 'security',
223 'cve'
224 ]
225 ]
226 if (jiraNamespace == 'PRODX') {
Ivan Udovichenkoadee8b52020-08-06 17:07:28 +0300227 basicIssueJSON['fields']['customfield_19000'] = [value:"${team_assignee}"]
228 basicIssueJSON['fields']['versions'] = [["name": "Backlog"]]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300229 }
Ivan Udovichenkoadee8b52020-08-06 17:07:28 +0300230 def post_issue_json = JsonOutput.toJson(basicIssueJSON)
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300231 def post_comment_json = """
232{
233 "body": "${jira_description}"
234}
235"""
236 def jira_key = cacheLookUp(dict, image_key, image.key)
237 if (jira_key[0] && jira_key[1] == 'na') {
238 def post_comment_response = callREST("${uri}/${jira_key[0]}/comment", auth, 'POST', post_comment_json)
239 if ( post_comment_response['responseCode'] == 201 ) {
240 def issueCommentJSON = new JsonSlurper().parseText(post_comment_response["responseText"])
Ivan Udovichenko99467752020-04-21 02:28:06 +0300241 print "\n\nComment was posted to ${jira_key[0]} for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300242 } else {
243 print "\nComment to ${jira_key[0]} Jira issue was not posted"
244 }
245 } else if (!jira_key[0]) {
246 def post_issue_response = callREST("${uri}/", auth, 'POST', post_issue_json)
247 if (post_issue_response['responseCode'] == 201) {
248 def issueJSON = new JsonSlurper().parseText(post_issue_response["responseText"])
249 dict = updateDictionary(issueJSON['key'], dict, uri, auth, jiraUserID)
Ivan Udovichenko99467752020-04-21 02:28:06 +0300250 print "\n\nJira issue was created ${issueJSON['key']} for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300251 } else {
252 print "\n${image.key} CVE issues were not published\n"
253 }
254 } else {
Ivan Udovichenko99467752020-04-21 02:28:06 +0300255 print "\n\nNothing to process for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300256 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300257 }
Ivan Udovichenko314a0732020-04-13 22:47:26 +0300258}
Ivan Udovichenko45252252020-04-14 22:45:53 +0300259
260def find_cves_by_severity(String reportJsonContent, String Severity) {
261 def cves = []
262 def reportJSON = new JsonSlurper().parseText(reportJsonContent)
263 reportJSON.each{
264 image ->
265 image.value.each{
266 pkg ->
267 pkg.value.each{
268 cve ->
269 if (cve[2]) {
270 if (cve[1].contains(Severity)) {
271 cves.add("${pkg.key} ${cve[0]} (${cve[2]})")
272 }
273 }
274 }
275 }
276 }
277 return cves
278}