Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 1 | #!groovy |
| 2 | |
Ivan Udovichenko | 314a073 | 2020-04-13 22:47:26 +0300 | [diff] [blame] | 3 | package com.mirantis.mk |
| 4 | |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 5 | import groovy.json.JsonSlurper |
Ivan Udovichenko | 6c33756 | 2020-08-06 16:22:26 +0300 | [diff] [blame] | 6 | import groovy.json.JsonOutput |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 7 | |
| 8 | def callREST (String uri, String auth, |
Ivan Udovichenko | 4525225 | 2020-04-14 22:45:53 +0300 | [diff] [blame] | 9 | 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 Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 26 | } |
| 27 | |
| 28 | def getTeam (String image = '') { |
| 29 | def team_assignee = '' |
| 30 | switch(image) { |
| 31 | case ~/^(tungsten|tungsten-operator)\/.*$/: |
| 32 | team_assignee = 'OpenContrail' |
| 33 | break |
Ivan Udovichenko | 8c78c35 | 2020-12-14 22:39:47 +0300 | [diff] [blame] | 34 | case ~/^(bm|general)\/.*$/: |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 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 |
Ivan Udovichenko | 5d755da | 2021-08-09 14:58:56 +0300 | [diff] [blame] | 46 | case ~/^(core|iam|lcm)\/.*$/: |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 47 | team_assignee = 'KaaS' |
| 48 | break |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 49 | default: |
| 50 | team_assignee = 'Release Engineering' |
| 51 | break |
| 52 | } |
| 53 | |
| 54 | return team_assignee |
| 55 | } |
| 56 | |
| 57 | def updateDictionary (String jira_issue_key, Map dict, String uri, String auth, String jira_user_id) { |
| 58 | def response = callREST("${uri}/${jira_issue_key}", auth) |
| 59 | if ( response['responseCode'] == 200 ) { |
| 60 | def issueJSON = new JsonSlurper().parseText(response["responseText"]) |
| 61 | if (issueJSON.containsKey('fields')) { |
| 62 | if (!dict.containsKey(jira_issue_key)) { |
| 63 | dict[jira_issue_key] = [ |
| 64 | summary : '', |
| 65 | description: '', |
| 66 | comments: [] |
| 67 | ] |
| 68 | } |
| 69 | if (issueJSON['fields'].containsKey('summary')){ |
| 70 | dict[jira_issue_key].summary = issueJSON['fields']['summary'] |
| 71 | } |
| 72 | if (issueJSON['fields'].containsKey('description')) { |
| 73 | dict[jira_issue_key].description = issueJSON['fields']['description'] |
| 74 | } |
| 75 | if (issueJSON['fields'].containsKey('comment') && issueJSON['fields']['comment']['comments']) { |
| 76 | issueJSON['fields']['comment']['comments'].each { |
| 77 | if (it.containsKey('author') && it['author'].containsKey('accountId') && it['author']['accountId'] == jira_user_id) { |
| 78 | dict[jira_issue_key]['comments'].add(it['body']) |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 | return dict |
| 85 | } |
| 86 | |
| 87 | def cacheLookUp(Map dict, String image_short_name, String image_full_name = '', String cve_id = '' ) { |
| 88 | def found_key = ['',''] |
| 89 | if (!found_key[0] && dict && image_short_name) { |
| 90 | dict.each { issue_key_name -> |
| 91 | if (!found_key[0]) { |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 92 | def s |
| 93 | if (image_short_name =~ /^mirantis(eng)?\//) { |
| 94 | def tmp_image_short_name = image_short_name.replaceAll(/^mirantis(eng)?\//, '') |
| 95 | s = dict[issue_key_name.key]['summary'] =~ /^\[mirantis(eng)?\/${tmp_image_short_name}(?=\])/ |
| 96 | } else { |
| 97 | s = dict[issue_key_name.key]['summary'] =~ /(?<=[\/\[])${image_short_name}(?=\])/ |
| 98 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 99 | if (s) { |
| 100 | if (image_full_name) { |
| 101 | def d = dict[issue_key_name.key]['description'] =~ /(?m)\b${image_full_name}\b/ |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 102 | if (d) { |
| 103 | found_key = [issue_key_name.key,''] |
| 104 | } else { |
| 105 | if (dict[issue_key_name.key]['comments']) { |
| 106 | def comment_match = false |
| 107 | dict[issue_key_name.key]['comments'].each{ comment -> |
| 108 | if (!comment_match) { |
| 109 | def c = comment =~ /(?m)\b${image_full_name}\b/ |
| 110 | if (c) { |
| 111 | comment_match = true |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | if (!comment_match) { |
| 116 | found_key = [issue_key_name.key,'na'] |
| 117 | } else { |
| 118 | found_key = [issue_key_name.key,''] |
| 119 | } |
| 120 | } else { |
| 121 | found_key = [issue_key_name.key,'na'] |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | return found_key |
| 130 | } |
| 131 | |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 132 | def getLatestAffectedVersion(cred, productName, defaultJiraAffectedVersion = 'Backlog') { |
| 133 | def filterName = '' |
| 134 | if (productName == 'mosk') { |
| 135 | filterName = 'MOSK' |
| 136 | } else if (productName == 'kaas') { |
| 137 | filterName = 'KaaS' |
| 138 | } else { |
| 139 | return defaultJiraAffectedVersion |
| 140 | } |
| 141 | |
Ivan Udovichenko | 4c08b8d | 2021-01-20 22:15:49 +0000 | [diff] [blame] | 142 | def search_api_url = "${cred.description}/rest/api/2/issue/createmeta?projectKeys=PRODX&issuetypeNames=Bug&expand=projects.issuetypes.fields" |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 143 | def response = callREST("${search_api_url}", "${cred.username}:${cred.password}", 'GET') |
| 144 | def InputJSON = new JsonSlurper().parseText(response["responseText"]) |
| 145 | def AffectedVersions = InputJSON['projects'][0]['issuetypes'][0]['fields']['versions']['allowedValues'] |
| 146 | |
| 147 | def versions = [] |
| 148 | AffectedVersions.each{ |
Ivan Udovichenko | 14e87f6 | 2021-06-01 17:20:39 +0300 | [diff] [blame] | 149 | // 'MOSK' doesn not contain 'released' field |
| 150 | if (productName != 'mosk' && it.containsKey('released') && it['released']) { |
| 151 | return |
| 152 | } |
| 153 | if (it.containsKey('name') && it['name'].startsWith(filterName)) { |
| 154 | def justVersion = it['name'].replaceAll(/.*_/, '') |
| 155 | justVersion = justVersion.replaceAll(/([0-9]+\.)([0-9])$/, '$10$2') |
| 156 | versions.add("${justVersion}`${it['name']}") |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 157 | } |
| 158 | } |
| 159 | if (versions) { |
Ivan Udovichenko | 14e87f6 | 2021-06-01 17:20:39 +0300 | [diff] [blame] | 160 | return versions.sort()[0].split('`')[-1] |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 161 | } |
| 162 | return defaultJiraAffectedVersion |
| 163 | } |
| 164 | |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 165 | def getNvdInfo(nvdApiUrl, cve, requestDelay = 1, requestRetryNum = 5, sleepTimeOnBan = 60) { |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 166 | def cveArr = [] |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 167 | sleep requestDelay |
| 168 | def response = callREST("${nvdApiUrl}/${cve}", '') |
| 169 | for (i = 0; i < requestRetryNum; i++) { |
| 170 | if (response['responseCode'] == 429) { |
| 171 | sleep sleepTimeOnBan |
| 172 | response = callREST("${nvdApiUrl}/${cve}", '') |
| 173 | } else { |
| 174 | return |
| 175 | } |
| 176 | } |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 177 | if (response['responseCode'] == 200) { |
| 178 | def InputJSON = new JsonSlurper().parseText(response["responseText"]) |
| 179 | if (InputJSON.containsKey('impact')) { |
| 180 | def cveImpact = InputJSON['impact'] |
| 181 | ['V3','V2'].each { |
| 182 | if (cveImpact.containsKey('baseMetric' + it)) { |
| 183 | if (cveImpact['baseMetric' + it].containsKey('cvss' + it)) { |
| 184 | if (cveImpact['baseMetric' + it]['cvss' + it].containsKey('baseScore')) { |
| 185 | def cveBaseSeverity = '' |
| 186 | if (cveImpact['baseMetric' + it]['cvss' + it].containsKey('baseSeverity')) { |
| 187 | cveBaseSeverity = cveImpact['baseMetric'+it]['cvss'+it]['baseSeverity'] |
| 188 | } |
| 189 | cveArr.add([it, cveImpact['baseMetric'+it]['cvss'+it]['baseScore'],cveBaseSeverity]) |
| 190 | } |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 191 | |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | return cveArr |
| 198 | } |
| 199 | |
| 200 | |
Ivan Udovichenko | b3fc9f7 | 2021-09-23 21:41:43 +0300 | [diff] [blame^] | 201 | def reportJiraTickets(String reportFileContents, String jiraCredentialsID, String jiraUserID, String productName = '', String ignoreImageListFileContents = '[]', Integer retryTry = 0, String nvdApiUrl = '', jiraNamespace = 'PRODX', nvdNistGovCveUrl = 'https://nvd.nist.gov/vuln/detail/') { |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 202 | |
| 203 | def dict = [:] |
| 204 | |
Ivan Udovichenko | 6c870b7 | 2020-04-14 11:31:51 +0300 | [diff] [blame] | 205 | def common = new com.mirantis.mk.Common() |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 206 | def cred = common.getCredentialsById(jiraCredentialsID) |
| 207 | def auth = "${cred.username}:${cred.password}" |
| 208 | def uri = "${cred.description}/rest/api/2/issue" |
| 209 | |
| 210 | def search_api_url = "${cred.description}/rest/api/2/search" |
| 211 | |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 212 | |
| 213 | def jqlStartAt = 0 |
| 214 | def jqlStep = 100 |
| 215 | def jqlProcessedItems = 0 |
| 216 | def jqlUnfinishedProcess = true |
| 217 | def jqlTotalItems = 0 |
| 218 | while (jqlUnfinishedProcess) { |
| 219 | def search_json = """ |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 220 | { |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 221 | "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 or status = 'Input Required')", "maxResults":-1, "startAt": ${jqlStartAt} |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 222 | } |
| 223 | """ |
| 224 | |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 225 | def response = callREST("${search_api_url}", auth, 'POST', search_json) |
| 226 | def InputJSON = new JsonSlurper().parseText(response["responseText"]) |
| 227 | if (InputJSON.containsKey('maxResults')){ |
| 228 | if (jqlStep > InputJSON['maxResults']) { |
| 229 | jqlStep = InputJSON['maxResults'] |
| 230 | } |
| 231 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 232 | |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 233 | jqlStartAt = jqlStartAt + jqlStep |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 234 | |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 235 | if (InputJSON.containsKey('total')){ |
| 236 | jqlTotalItems = InputJSON['total'] |
| 237 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 238 | |
Ivan Udovichenko | 77fd79e | 2021-09-20 13:15:28 +0300 | [diff] [blame] | 239 | if (InputJSON.containsKey('issues')){ |
Ivan Udovichenko | 16568a6 | 2021-09-16 12:50:59 +0300 | [diff] [blame] | 240 | if (!InputJSON['issues'] && retryTry != 0) { |
| 241 | throw new Exception('"issues" list is empty') |
| 242 | } |
| 243 | } else { |
| 244 | throw new Exception('Returned JSON from jql does not contain "issues" section') |
| 245 | } |
| 246 | print 'Temporal debug information:' |
| 247 | InputJSON['issues'].each { |
| 248 | print it['key'] + ' -> ' + it['fields']['summary'] |
| 249 | } |
| 250 | |
| 251 | InputJSON['issues'].each { |
| 252 | dict[it['key']] = [ |
| 253 | summary : '', |
| 254 | description: '', |
| 255 | comments: [] |
| 256 | ] |
| 257 | } |
| 258 | |
| 259 | InputJSON['issues'].each { jira_issue -> |
| 260 | dict = updateDictionary(jira_issue['key'], dict, uri, auth, jiraUserID) |
| 261 | jqlProcessedItems = jqlProcessedItems + 1 |
| 262 | } |
| 263 | if (jqlProcessedItems >= jqlTotalItems) { |
| 264 | jqlUnfinishedProcess = false |
| 265 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 266 | } |
| 267 | |
Ivan Udovichenko | 6c870b7 | 2020-04-14 11:31:51 +0300 | [diff] [blame] | 268 | def reportJSON = new JsonSlurper().parseText(reportFileContents) |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 269 | def imageDict = [:] |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 270 | reportJSON.each{ |
| 271 | image -> |
| 272 | if ("${image.value}".contains('issues')) { return } |
| 273 | image.value.each{ |
| 274 | pkg -> |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 275 | pkg.value.each{ |
| 276 | cve -> |
| 277 | if (cve[2] && (cve[1].contains('High') || cve[1].contains('Critical'))) { |
Ivan Udovichenko | c774c6e | 2020-04-15 01:55:41 +0300 | [diff] [blame] | 278 | if (!imageDict.containsKey(image.key)) { |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 279 | imageDict.put(image.key, [:]) |
| 280 | } |
| 281 | if (!imageDict[image.key].containsKey(pkg.key)) { |
| 282 | imageDict[image.key].put(pkg.key, []) |
| 283 | } |
Ivan Udovichenko | 5c60cde | 2021-08-12 23:52:58 +0300 | [diff] [blame] | 284 | imageDict[image.key][pkg.key].add("[${cve[0]}|${cve[4]}] (${cve[2]}) (${cve[3]}) | ${cve[5]}") |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 285 | } |
| 286 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 287 | } |
| 288 | } |
| 289 | |
Ivan Udovichenko | 5e4ae9c | 2020-12-26 10:56:48 +0300 | [diff] [blame] | 290 | def affectedVersion = '' |
| 291 | if (jiraNamespace == 'PRODX') { |
| 292 | affectedVersion = getLatestAffectedVersion(cred, productName) |
| 293 | } |
| 294 | |
Ivan Udovichenko | 83b7ffc | 2021-05-07 12:50:16 +0300 | [diff] [blame] | 295 | def ignoreImageList = new JsonSlurper().parseText(ignoreImageListFileContents) |
| 296 | |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 297 | def jira_summary = '' |
| 298 | def jira_description = '' |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 299 | def jira_description_nvd_scoring = [] |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 300 | imageDict.each{ |
| 301 | image -> |
| 302 | def image_key = image.key.replaceAll(/(^[a-z0-9-.]+.mirantis.(net|com)\/|:.*$)/, '') |
Ivan Udovichenko | 83b7ffc | 2021-05-07 12:50:16 +0300 | [diff] [blame] | 303 | |
| 304 | // Ignore images listed |
| 305 | if ((image.key in ignoreImageList) || (image.key.replaceAll(/:.*$/, '') in ignoreImageList)) { |
| 306 | print "\n\nIgnoring ${image.key} as it has been found in Docker image ignore list\n" |
| 307 | return |
| 308 | } |
| 309 | |
Ivan Udovichenko | 6c33756 | 2020-08-06 16:22:26 +0300 | [diff] [blame] | 310 | // Below change was produced due to other workflow for UCP Docker images (RE-274) |
Ivan Udovichenko | 9721c31 | 2020-10-01 23:50:38 +0300 | [diff] [blame] | 311 | if (image_key.startsWith('lcm/docker/ucp')) { |
| 312 | return |
Ivan Udovichenko | 6bc5e18 | 2020-10-30 02:57:56 +0300 | [diff] [blame] | 313 | } else if (image_key.startsWith('mirantis/ucp') || image_key.startsWith('mirantiseng/ucp')) { |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 314 | jiraNamespace = 'MKE' |
Ivan Udovichenko | e0ea494 | 2021-02-18 17:50:20 +0300 | [diff] [blame] | 315 | } else if (image_key.startsWith('mirantis/dtr') || image_key.startsWith('mirantiseng/dtr')) { |
| 316 | jiraNamespace = 'ENGDTR' |
Ivan Udovichenko | fe3f11e | 2020-09-29 17:31:38 +0300 | [diff] [blame] | 317 | } else { |
| 318 | jiraNamespace = 'PRODX' |
| 319 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 320 | jira_summary = "[${image_key}] Found CVEs in Docker image" |
Ivan Udovichenko | 6256361 | 2020-10-31 03:46:23 +0300 | [diff] [blame] | 321 | jira_description = "${image.key}\n" |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 322 | def filter_mke_severity = false |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 323 | image.value.each{ |
| 324 | pkg -> |
Ivan Udovichenko | 6256361 | 2020-10-31 03:46:23 +0300 | [diff] [blame] | 325 | jira_description += "__* ${pkg.key}\n" |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 326 | pkg.value.each{ |
| 327 | cve -> |
Ivan Udovichenko | 6256361 | 2020-10-31 03:46:23 +0300 | [diff] [blame] | 328 | jira_description += "________${cve}\n" |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 329 | if (nvdApiUrl) { |
| 330 | def cveId = cve.replaceAll(/(^\[|\|.*$)/, '') |
| 331 | if (cveId.startsWith('CVE-')) { |
| 332 | jira_description_nvd_scoring = getNvdInfo(nvdApiUrl, cveId) |
| 333 | jira_description_nvd_scoring.each { |
| 334 | jira_description += 'CVSS ' + it.join(' ') + '\n' |
| 335 | // According to Vikram there will be no fixes for |
| 336 | // CVEs with CVSS base score below 7 |
| 337 | if (jiraNamespace == 'MKE' && it[0] == 'V3' && it[1].toInteger() >= 7) { |
| 338 | filter_mke_severity = true |
| 339 | } |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 340 | } |
Ivan Udovichenko | b3fc9f7 | 2021-09-23 21:41:43 +0300 | [diff] [blame^] | 341 | if (filter_mke_severity) { |
| 342 | jira_description += nvdNistGovCveUrl + cveId + '\n' |
| 343 | } |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 344 | } |
| 345 | } else { |
Ivan Udovichenko | dd63d66 | 2021-09-23 19:35:38 +0300 | [diff] [blame] | 346 | print 'nvdApiUrl var is not specified.' |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 347 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 348 | } |
| 349 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 350 | |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 351 | if (filter_mke_severity) { |
| 352 | print "\n\nIgnoring ${image.key} as it does not have CVEs with CVSS base score >7\n" |
| 353 | print jira_description |
| 354 | return |
| 355 | } |
| 356 | |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 357 | def team_assignee = getTeam(image_key) |
| 358 | |
Ivan Udovichenko | adee8b5 | 2020-08-06 17:07:28 +0300 | [diff] [blame] | 359 | def basicIssueJSON = new JsonSlurper().parseText('{"fields": {}}') |
Ivan Udovichenko | 6c33756 | 2020-08-06 16:22:26 +0300 | [diff] [blame] | 360 | |
Ivan Udovichenko | adee8b5 | 2020-08-06 17:07:28 +0300 | [diff] [blame] | 361 | basicIssueJSON['fields'] = [ |
Ivan Udovichenko | fe3f11e | 2020-09-29 17:31:38 +0300 | [diff] [blame] | 362 | project:[ |
| 363 | key:"${jiraNamespace}" |
| 364 | ], |
Ivan Udovichenko | 6c33756 | 2020-08-06 16:22:26 +0300 | [diff] [blame] | 365 | summary:"${jira_summary}", |
| 366 | description:"${jira_description}", |
| 367 | issuetype:[ |
Ivan Udovichenko | fe3f11e | 2020-09-29 17:31:38 +0300 | [diff] [blame] | 368 | name:'Bug' |
Ivan Udovichenko | 6c33756 | 2020-08-06 16:22:26 +0300 | [diff] [blame] | 369 | ], |
| 370 | labels:[ |
| 371 | 'security', |
| 372 | 'cve' |
| 373 | ] |
| 374 | ] |
| 375 | if (jiraNamespace == 'PRODX') { |
Ivan Udovichenko | adee8b5 | 2020-08-06 17:07:28 +0300 | [diff] [blame] | 376 | basicIssueJSON['fields']['customfield_19000'] = [value:"${team_assignee}"] |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 377 | basicIssueJSON['fields']['versions'] = [["name": affectedVersion]] |
Ivan Udovichenko | 49955b2 | 2021-08-19 23:22:47 +0300 | [diff] [blame] | 378 | if (image_key.startsWith('lcm/')) { |
| 379 | basicIssueJSON['fields']['components'] = [["name": 'KaaS: LCM']] |
| 380 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 381 | } |
Ivan Udovichenko | 3163123 | 2021-09-22 13:54:02 +0300 | [diff] [blame] | 382 | |
| 383 | if (jiraNamespace == 'MKE') { |
| 384 | // Assign issues by default to Vikram bir Singh, as it was asked by him |
| 385 | basicIssueJSON['fields']['assignee'] = ['accountId': '5ddd4d67b95b180d17cecc67'] |
| 386 | } |
| 387 | |
Ivan Udovichenko | adee8b5 | 2020-08-06 17:07:28 +0300 | [diff] [blame] | 388 | def post_issue_json = JsonOutput.toJson(basicIssueJSON) |
Ivan Udovichenko | 6256361 | 2020-10-31 03:46:23 +0300 | [diff] [blame] | 389 | def jira_comment = jira_description.replaceAll(/\n/, '\\\\n') |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 390 | def post_comment_json = """ |
| 391 | { |
Ivan Udovichenko | 6256361 | 2020-10-31 03:46:23 +0300 | [diff] [blame] | 392 | "body": "${jira_comment}" |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 393 | } |
| 394 | """ |
| 395 | def jira_key = cacheLookUp(dict, image_key, image.key) |
| 396 | if (jira_key[0] && jira_key[1] == 'na') { |
| 397 | def post_comment_response = callREST("${uri}/${jira_key[0]}/comment", auth, 'POST', post_comment_json) |
| 398 | if ( post_comment_response['responseCode'] == 201 ) { |
| 399 | def issueCommentJSON = new JsonSlurper().parseText(post_comment_response["responseText"]) |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 400 | print "\n\nComment was posted to ${jira_key[0]} ${affectedVersion} for ${image_key} and ${image.key}" |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 401 | } else { |
| 402 | print "\nComment to ${jira_key[0]} Jira issue was not posted" |
| 403 | } |
| 404 | } else if (!jira_key[0]) { |
| 405 | def post_issue_response = callREST("${uri}/", auth, 'POST', post_issue_json) |
| 406 | if (post_issue_response['responseCode'] == 201) { |
| 407 | def issueJSON = new JsonSlurper().parseText(post_issue_response["responseText"]) |
| 408 | dict = updateDictionary(issueJSON['key'], dict, uri, auth, jiraUserID) |
Ivan Udovichenko | 6739855 | 2020-12-25 20:13:35 +0300 | [diff] [blame] | 409 | print "\n\nJira issue was created ${issueJSON['key']} ${affectedVersion} for ${image_key} and ${image.key}" |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 410 | } else { |
| 411 | print "\n${image.key} CVE issues were not published\n" |
| 412 | } |
| 413 | } else { |
Ivan Udovichenko | 9946775 | 2020-04-21 02:28:06 +0300 | [diff] [blame] | 414 | print "\n\nNothing to process for ${image_key} and ${image.key}" |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 415 | } |
Ivan Udovichenko | 894fd8a | 2020-04-13 17:24:50 +0300 | [diff] [blame] | 416 | } |
Ivan Udovichenko | 314a073 | 2020-04-13 22:47:26 +0300 | [diff] [blame] | 417 | } |
Ivan Udovichenko | 4525225 | 2020-04-14 22:45:53 +0300 | [diff] [blame] | 418 | |
| 419 | def find_cves_by_severity(String reportJsonContent, String Severity) { |
| 420 | def cves = [] |
| 421 | def reportJSON = new JsonSlurper().parseText(reportJsonContent) |
| 422 | reportJSON.each{ |
| 423 | image -> |
| 424 | image.value.each{ |
| 425 | pkg -> |
| 426 | pkg.value.each{ |
| 427 | cve -> |
| 428 | if (cve[2]) { |
| 429 | if (cve[1].contains(Severity)) { |
| 430 | cves.add("${pkg.key} ${cve[0]} (${cve[2]})") |
| 431 | } |
| 432 | } |
| 433 | } |
| 434 | } |
| 435 | } |
| 436 | return cves |
| 437 | } |