blob: bb8e3613acb6b462f3e05272b358d1712f8d84b3 [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
6
7def callREST (String uri, String auth,
Ivan Udovichenko45252252020-04-14 22:45:53 +03008 String method = 'GET', String message = null) {
9 String authEnc = auth.bytes.encodeBase64()
10 def req = new URL(uri).openConnection()
11 req.setRequestMethod(method)
12 req.setRequestProperty('Content-Type', 'application/json')
13 req.setRequestProperty('Authorization', "Basic ${authEnc}")
14 if (message) {
15 req.setDoOutput(true)
16 req.getOutputStream().write(message.getBytes('UTF-8'))
17 }
18 Integer responseCode = req.getResponseCode()
19 String responseText = ''
20 if (responseCode == 200 || responseCode == 201) {
21 responseText = req.getInputStream().getText()
22 }
23 req = null
24 return [ 'responseCode': responseCode, 'responseText': responseText ]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +030025}
26
27def getTeam (String image = '') {
28 def team_assignee = ''
29 switch(image) {
30 case ~/^(tungsten|tungsten-operator)\/.*$/:
31 team_assignee = 'OpenContrail'
32 break
33 case ~/^bm\/.*$/:
34 team_assignee = 'BM/OS (KaaS BM)'
35 break
36 case ~/^openstack\/.*$/:
37 team_assignee = 'OpenStack hardening'
38 break
39 case ~/^stacklight\/.*$/:
40 team_assignee = 'Stacklight LMA'
41 break
42 case ~/^ceph\/.*$/:
43 team_assignee = 'Storage'
44 break
45 case ~/^iam\/.*$/:
46 team_assignee = 'KaaS'
47 break
48 case ~/^lcm\/.*$/:
49 team_assignee = 'Kubernetes'
50 break
51 default:
52 team_assignee = 'Release Engineering'
53 break
54 }
55
56 return team_assignee
57}
58
59def updateDictionary (String jira_issue_key, Map dict, String uri, String auth, String jira_user_id) {
60 def response = callREST("${uri}/${jira_issue_key}", auth)
61 if ( response['responseCode'] == 200 ) {
62 def issueJSON = new JsonSlurper().parseText(response["responseText"])
63 if (issueJSON.containsKey('fields')) {
64 if (!dict.containsKey(jira_issue_key)) {
65 dict[jira_issue_key] = [
66 summary : '',
67 description: '',
68 comments: []
69 ]
70 }
71 if (issueJSON['fields'].containsKey('summary')){
72 dict[jira_issue_key].summary = issueJSON['fields']['summary']
73 }
74 if (issueJSON['fields'].containsKey('description')) {
75 dict[jira_issue_key].description = issueJSON['fields']['description']
76 }
77 if (issueJSON['fields'].containsKey('comment') && issueJSON['fields']['comment']['comments']) {
78 issueJSON['fields']['comment']['comments'].each {
79 if (it.containsKey('author') && it['author'].containsKey('accountId') && it['author']['accountId'] == jira_user_id) {
80 dict[jira_issue_key]['comments'].add(it['body'])
81 }
82 }
83 }
84 }
85 }
86 return dict
87}
88
89def cacheLookUp(Map dict, String image_short_name, String image_full_name = '', String cve_id = '' ) {
90 def found_key = ['','']
91 if (!found_key[0] && dict && image_short_name) {
92 dict.each { issue_key_name ->
93 if (!found_key[0]) {
94 def s = dict[issue_key_name.key]['summary'] =~ /\b${image_short_name}\b/
95 if (s) {
96 if (image_full_name) {
97 def d = dict[issue_key_name.key]['description'] =~ /(?m)\b${image_full_name}\b/
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +030098 if (d) {
99 found_key = [issue_key_name.key,'']
100 } else {
101 if (dict[issue_key_name.key]['comments']) {
102 def comment_match = false
103 dict[issue_key_name.key]['comments'].each{ comment ->
104 if (!comment_match) {
105 def c = comment =~ /(?m)\b${image_full_name}\b/
106 if (c) {
107 comment_match = true
108 }
109 }
110 }
111 if (!comment_match) {
112 found_key = [issue_key_name.key,'na']
113 } else {
114 found_key = [issue_key_name.key,'']
115 }
116 } else {
117 found_key = [issue_key_name.key,'na']
118 }
119 }
120 }
121 }
122 }
123 }
124 }
125 return found_key
126}
127
Ivan Udovichenko6c870b72020-04-14 11:31:51 +0300128def reportJiraTickets(String reportFileContents, String jiraCredentialsID, String jiraUserID) {
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300129
130 def dict = [:]
131
Ivan Udovichenko6c870b72020-04-14 11:31:51 +0300132 def common = new com.mirantis.mk.Common()
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300133 def cred = common.getCredentialsById(jiraCredentialsID)
134 def auth = "${cred.username}:${cred.password}"
135 def uri = "${cred.description}/rest/api/2/issue"
136
137 def search_api_url = "${cred.description}/rest/api/2/search"
138
139 def search_json = """
140{
141 "jql": "reporter = ${jiraUserID} and (labels = cve and labels = security) and (status = 'To Do' or status = 'For Triage' or status = Open or status = 'In Progress')"
142}
143"""
144
145 def response = callREST("${search_api_url}", auth, 'POST', search_json)
146
147 def InputJSON = new JsonSlurper().parseText(response["responseText"])
148
149 InputJSON['issues'].each {
150 dict[it['key']] = [
151 summary : '',
152 description: '',
153 comments: []
154 ]
155 }
156
157 InputJSON['issues'].each { jira_issue ->
158 dict = updateDictionary(jira_issue['key'], dict, uri, auth, jiraUserID)
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300159 }
160
Ivan Udovichenko6c870b72020-04-14 11:31:51 +0300161 def reportJSON = new JsonSlurper().parseText(reportFileContents)
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300162 def imageDict = [:]
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300163 reportJSON.each{
164 image ->
165 if ("${image.value}".contains('issues')) { return }
166 image.value.each{
167 pkg ->
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300168 pkg.value.each{
169 cve ->
170 if (cve[2] && (cve[1].contains('High') || cve[1].contains('Critical'))) {
Ivan Udovichenkoc774c6e2020-04-15 01:55:41 +0300171 if (!imageDict.containsKey(image.key)) {
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300172 imageDict.put(image.key, [:])
173 }
174 if (!imageDict[image.key].containsKey(pkg.key)) {
175 imageDict[image.key].put(pkg.key, [])
176 }
Ivan Udovichenkob2e52352020-06-29 18:03:56 +0300177 imageDict[image.key][pkg.key].add("[${cve[0]}|${cve[4]}] (${cve[2]}) (${cve[3]})")
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300178 }
179 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300180 }
181 }
182
183 def jira_summary = ''
184 def jira_description = ''
185 imageDict.each{
186 image ->
187 def image_key = image.key.replaceAll(/(^[a-z0-9-.]+.mirantis.(net|com)\/|:.*$)/, '')
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300188 jira_summary = "[${image_key}] Found CVEs in Docker image"
Ivan Udovichenkob2e52352020-06-29 18:03:56 +0300189 jira_description = "${image.key}\\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300190 image.value.each{
191 pkg ->
Ivan Udovichenkob2e52352020-06-29 18:03:56 +0300192 jira_description += "__* ${pkg.key}\\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300193 pkg.value.each{
194 cve ->
Ivan Udovichenkob2e52352020-06-29 18:03:56 +0300195 jira_description += "________${cve}\\n"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300196 }
197 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300198
199 def team_assignee = getTeam(image_key)
200
201 def post_issue_json = """
202{
203 "fields": {
204 "project": {
205 "key": "PRODX"
206 },
207 "summary": "${jira_summary}",
208 "description": "${jira_description}",
209 "issuetype": {
210 "name": "Bug"
211 },
212 "labels": [
213 "security",
214 "cve"
215 ],
216 "customfield_19000": {
217 "value": "${team_assignee}"
218 },
219 "versions": [
220 {
221 "name": "Backlog"
222 }
223 ]
224 }
225}
226"""
227 def post_comment_json = """
228{
229 "body": "${jira_description}"
230}
231"""
232 def jira_key = cacheLookUp(dict, image_key, image.key)
233 if (jira_key[0] && jira_key[1] == 'na') {
234 def post_comment_response = callREST("${uri}/${jira_key[0]}/comment", auth, 'POST', post_comment_json)
235 if ( post_comment_response['responseCode'] == 201 ) {
236 def issueCommentJSON = new JsonSlurper().parseText(post_comment_response["responseText"])
Ivan Udovichenko99467752020-04-21 02:28:06 +0300237 print "\n\nComment was posted to ${jira_key[0]} for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300238 } else {
239 print "\nComment to ${jira_key[0]} Jira issue was not posted"
240 }
241 } else if (!jira_key[0]) {
242 def post_issue_response = callREST("${uri}/", auth, 'POST', post_issue_json)
243 if (post_issue_response['responseCode'] == 201) {
244 def issueJSON = new JsonSlurper().parseText(post_issue_response["responseText"])
245 dict = updateDictionary(issueJSON['key'], dict, uri, auth, jiraUserID)
Ivan Udovichenko99467752020-04-21 02:28:06 +0300246 print "\n\nJira issue was created ${issueJSON['key']} for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300247 } else {
248 print "\n${image.key} CVE issues were not published\n"
249 }
250 } else {
Ivan Udovichenko99467752020-04-21 02:28:06 +0300251 print "\n\nNothing to process for ${image_key} and ${image.key}"
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300252 }
Ivan Udovichenko894fd8a2020-04-13 17:24:50 +0300253 }
Ivan Udovichenko314a0732020-04-13 22:47:26 +0300254}
Ivan Udovichenko45252252020-04-14 22:45:53 +0300255
256def find_cves_by_severity(String reportJsonContent, String Severity) {
257 def cves = []
258 def reportJSON = new JsonSlurper().parseText(reportJsonContent)
259 reportJSON.each{
260 image ->
261 image.value.each{
262 pkg ->
263 pkg.value.each{
264 cve ->
265 if (cve[2]) {
266 if (cve[1].contains(Severity)) {
267 cves.add("${pkg.key} ${cve[0]} (${cve[2]})")
268 }
269 }
270 }
271 }
272 }
273 return cves
274}