"""Jira API logic.""" import base64 import json import requests from main import config from main.constants import GITHUB_VULN_SCAN_LABEL def create_issue(scan_result, project_key, repo): """Create issue.""" auth_token = get_auth_token(config.JIRA_USER_EMAIL, config.JIRA_TOKEN) existing_issue = post( f'{config.JIRA_DOMAIN_URL}/rest/api/3/search', { 'jql': f'status != "Done" AND project = {project_key} AND summary ~ "{repo}" AND labels IN ({GITHUB_VULN_SCAN_LABEL})' }, auth_token ) if existing_issue and existing_issue.json().get('issues'): # update jira issue description issue_key = existing_issue.json()['issues'][0]['key'] data = { 'fields': { 'description': format_issue_description(scan_result, repo) }, 'update': { 'labels': [ { 'add': GITHUB_VULN_SCAN_LABEL } ], } } put(f'{config.JIRA_DOMAIN_URL}/rest/api/3/issue/{issue_key}', data, auth_token) return issue_key else: # create jira issue summary = f'Resolve github vulnerability scan: {repo}' data = format_data( summary=summary, issuetype_id=config.JIRA_ISSUETYPE_ID, project_key=project_key, scan_result=scan_result, repo=repo ) resp = post(f'{config.JIRA_DOMAIN_URL}/rest/api/3/issue', data, auth_token) if not resp: return None return resp.json().get("key") def post(url, data, auth_token): """Jira post request.""" headers = { 'Authorization': f'Basic {auth_token}', 'Content-Type': 'application/json', } resp = requests.post(url, headers=headers, data=json.dumps(data)) if resp.status_code >= 400: return None return resp def put(url, data, auth_token): """Jira put request.""" headers = { 'Authorization': f'Basic {auth_token}', 'Content-Type': 'application/json', } resp = requests.put(url, headers=headers, data=json.dumps(data)) if resp.status_code >= 400: return None return resp def get_auth_token(email, jira_token): """Get base64 encoded auth.""" bytes = base64.b64encode(str.encode(f'{email}:{jira_token}')) return bytes.decode() def get_table_row(scan_result_row): """Get table row.""" return { 'type': 'tableRow', 'content': [{ 'type': 'tableCell', 'attrs': {}, 'content': [ { 'type': 'paragraph', 'content': [ { 'type': 'text', 'text': cell_text } ] } ] } for cell_text in scan_result_row] } def get_table_header(scan_result_header): """Get table header.""" return { 'type': 'tableRow', 'content': [ { 'type': 'tableHeader', 'attrs': {}, 'content': [ { 'type': 'paragraph', 'content': [ { 'type': 'text', 'text': column_title, 'marks': [ { 'type': 'strong' } ] } ] } ] } for column_title in scan_result_header ] } def format_issue_description(scan_result, repo): """Format description.""" table_header = get_table_header(scan_result[0]) table_data = [get_table_row(entry) for entry in scan_result[1:]] result = { 'type': 'doc', 'version': 1, 'content': [ { 'type': 'paragraph', 'content': [ { 'text': f'The following vulnerabilities were found in {repo}', 'type': 'text' } ] }, { 'type': 'table', 'attrs': { 'isNumberColumnEnabled': False, 'layout': 'default', 'localId': '7f9b774b-b396-46bb-9723-a594147ab951', 'width': 760 }, 'content': [table_header, *table_data] } ] } return result def format_data(summary, issuetype_id, project_key,scan_result, repo): """Format issue data.""" return { 'fields': { 'summary': summary, 'issuetype': { 'id': str(issuetype_id) }, 'project': { 'key': project_key }, 'description': format_issue_description(scan_result, repo) }, 'update': { 'labels': [ { 'add': GITHUB_VULN_SCAN_LABEL } ], } }