class JiraCI: resolution_state = {"fixed": "1", "wont fixed": "2", "duplicate": "3", "incomplete": "4", "cannot reproduce": "5", "not a bug": "6", "done": "7"} def __init__(self, jira_url, login, password): if version_info[1] <= 6: options = jira_url else: options = {"server": jira_url} self.jira = JIRA(options, basic_auth=(login, password)) @staticmethod def debug_jira(text): stdout.write("[DEBUG JIRA]: {0}\n".format(text)) def check_issue_exist(self, issue_id): try: self.jira.issue(issue_id) except JIRAError as e: print "[-] : {0} - {1}".format(issue_id, e.text) return False else: return True def check_issue_state(self, issue_id, issue_state): jira_issue = self.jira.issue(issue_id) if jira_issue.fields.status.name.lower() == issue_state.lower(): return True else: return False def add_comment(self, issue_id, comment, formatting=False): jira_issue = self.jira.issue(issue_id) if formatting: comment = "{code}" + comment + "{code}" if not self.check_comment_exist(issue_id, comment): self.jira.add_comment(jira_issue, comment) self.debug_jira("Comment (for {0}) : {1} added".format(issue_id, comment.rstrip())) else: self.debug_jira("Comment (for {0}) : {1} already exist".format(issue_id, comment.rstrip())) def assign_issue(self, issue_id, assigned_user): jira_issue = self.jira.issue(issue_id) jira_issue.update(assignee={"name": assigned_user}) def add_link(self, issue_id, title, url): url_object = {"url": url, "title": title} if not self.check_link_exist(issue_id, title, url): self.jira.add_remote_link(issue_id, url_object) self.debug_jira("Link (for {0}) : {1} added".format(issue_id, url)) else: self.debug_jira("Link (for {0}) : {1} already exist".format(issue_id, url)) def resolve_issue_to_reporter(self, issue_id): reporter = self.get_reporter_issue(issue_id) self.jira.transition_issue(issue_id, "5", resolution={"id": self.resolution_state["fixed"]}) self.assign_issue(issue_id, reporter) def get_reporter_issue(self, issue_id): jira_issue = self.jira.issue(issue_id) return jira_issue.fields.reporter.name def check_comment_exist(self, issue_id, new_comment): comments = [c.body for c in self.jira.comments(issue_id)] if new_comment in comments: return True return False def check_link_exist(self, issue_id, title, url): links = [l.raw["object"] for l in self.jira.remote_links(issue_id)] for link in links: if link["title"] == title and link["url"] == url: return True return False def resolve_from_git(self, issue_id, short_commit_message, title_url, package_url): if self.check_issue_exist(issue_id): if not self.check_issue_state(issue_id, "resolved"): self.resolve_issue_to_reporter(issue_id) self.debug_jira("Issue {0} already resolve".format(issue_id)) else: self.debug_jira("Issue {0} resolved".format(issue_id)) self.add_link(issue_id, title_url, package_url) self.add_comment(issue_id, short_commit_message, formatting=True) def refer_from_git(self, issue_id, commit_message): if self.check_issue_exist(issue_id): self.add_comment(issue_id, commit_message, formatting=True)
# Find all comments made by Atlassians on this issue. atl_comments = [comment for comment in issue.fields.comment.comments if re.search(r'@atlassian.com$', comment.author.emailAddress)] # Add a comment to the issue. jira.add_comment(issue, 'Comment text') # Change the issue's summary and description. issue.update( summary="I'm different!", description='Changed the summary to be different.') # Change the issue without sending updates issue.update(notify=False, description='Quiet summary update.') # You can update the entire labels field like this issue.update(labels=['AAA', 'BBB']) # Or modify the List of existing labels. The new label is unicode with no # spaces issue.fields.labels.append(u'new_text') issue.update(fields={"labels": issue.fields.labels}) # Send the issue away for good. issue.delete() # Linking a remote jira issue (needs applinks to be configured to work) issue = jira.issue('JRA-1330') issue2 = jira.issue('XX-23') # could also be another instance jira.add_remote_link(issue, issue2)
# Get all projects viewable by anonymous users. projects = jira.projects() print (projects) # Sort available project keys, then return the second, third, and fourth keys. keys = sorted([project.key for project in projects])[2:5] # Get an issue. issue = jira.issue('JRA-1330') # Find all comments made by Atlassians on this issue. atl_comments = [comment for comment in issue.fields.comment.comments if re.search(r'@atlassian.com$', comment.author.emailAddress)] # Add a comment to the issue. jira.add_comment(issue, 'Comment text') # Change the issue's summary and description. issue.update( summary="I'm different!", description='Changed the summary to be different.') # Change the issue without sending updates issue.update(notify=False, description='Quiet summary update.') # You can update the entire labels field like this issue.update(labels=['AAA', 'BBB']) # Or modify the List of existing labels. The new label is unicode with no # spaces issue.fields.labels.append(u'new_text') issue.update(fields={"labels": issue.fields.labels}) # Send the issue away for good. issue.delete() # Linking a remote jira issue (needs applinks to be configured to work) issue = jira.issue('JRA-1330') issue2 = jira.issue('XX-23') # could also be another instance jira.add_remote_link(issue, issue2)
class JiraIssues(object): APPLICATION = {"type": "www.hackerone.comr", "name": "Hacker One"} SCOPE = ''' h4.Scope ---- asset type: %(type)s asset identifier: %(identifier)s\n''' DESCRIPTION = ''' h4.Report Info ---- Report State: %(state)s Reporter: %(reporter)s Assignee: %(assignee)s Report Created: %(created)s Report Last Activity: %(last_activity)s h4.Weakness ---- name: %(name)s description: %(w_description)s id: %(id)s h4.Severity ---- rating: %(rating)s score: %(score)s' h4.Description ---- %(description)s ''' def __init__(self, server, username, password, project): """Inits jira client. This current setup requires a jira username to be setup with the appropriate permissions in the jira project :type server: string :param server: jira url :type username: string :param username: token :type password: string :param password: jira username password :type project: string :param project: jira project """ self.__jira_server = server self.__username = username self.__password = password self.jira_project = project self._init_jira_client() def _init_jira_client(self): options = {'server': self.__jira_server} def create_custom_field(fields=None): url = self._get_url('field') r = self._session.post(url, data=json.dumps(fields)) if r.status_code != 201: raise JIRAError(r.status_code, request=r) return r # Jira library doesn't have method for creating custom fields setattr(JIRA, 'create_custom_field', create_custom_field) self.jira_client = JIRA(options, basic_auth=(self.__username, self.__password)) def get_jira_projects(self): return self.jira_client.projects() def create_project(self, key, name, jira_type="Software"): return self.jira_client.create_project(key, name, jira_type) def get_jira_issue(self, report): """ Return Jira Issue based on HackerOne Report issue_tracker_reference_id :type report: h1.models.Report :param report: hackerone report :return: Jira Issue """ try: return self.jira_client.issue(report.issue_tracker_reference_id) except JIRAError as e: if e.text == "Issue Does Not Exist": return None else: raise @staticmethod def _get_jira_summary(report): return "%s - %s" % (report.id, report.title) def _get_jira_description(self, report): return self.DESCRIPTION % { 'description': report.vulnerability_information, 'reporter': report.reporter.name, 'assignee': report.assignee.name if report.assignee is not None else "", 'state': report.state, 'created': report.created_at, 'last_activity': report.last_activity_at, 'name': report.weakness.name, 'w_description': report.weakness.description, 'id': report.weakness.external_id, 'rating': report.severity.rating, 'score': report.severity.score } def create_jira_issue(self, report): """ Create Jira Issue https://developer.atlassian.com/server/jira/platform/jira-rest-api-example-create-issue-7897248/ :type report: h1.models.Report :param report: hackerone report :type :return: string :return: Jira ID """ issue_dict = { 'project': { 'key': self.jira_project }, 'summary': self._get_jira_summary(report), 'description': self._get_jira_description(report), 'issuetype': { 'name': 'Bug' }, 'labels': ['hackerOne'] } return self.jira_client.create_issue(fields=issue_dict, prefetch=True) def update_jira_issue(self, report, jira): fields = {} summary = self._get_jira_summary(report) if jira.fields.summary != summary: fields['summary'] = summary description = self._get_jira_description(report) if jira.fields.description != description: fields['description'] = description if fields: logging.info("Updating Existing Jira Issue: %s" % fields.keys()) jira.update(fields=fields) def search_for_jira_issues(self, report_id): """ Perform a Jira query search using JQL :param report_id: hacker one report id :return: returns jira issue match """ return self.jira_client.search_issues( '''project = %s AND summary ~ "%s"''' % (self.jira_project, report_id), maxResults=1) def get_fields(self): return self.jira_client.fields() def create_custom_field(self, fields): return self.jira_client.create_custom_field(fields) def get_remote_links(self, jira): return self.jira_client.remote_links(jira) def add_remote_link(self, report, jira, relationship="Relates"): links = set() # note all rmeote links have to have a global id for link in self.get_remote_links(jira): if hasattr(link, 'globalId'): links.add(link.globalId) if report.id not in links: destination = {'url': report.html_url, 'title': report.title} return self.jira_client.add_remote_link(jira, destination, report.id, self.APPLICATION, relationship) def add_simple_link(self, report, jira): """https://developer.atlassian.com/server/jira/platform/jira-rest-api-for-remote-issue-links/""" link = {'url': report.html_url, 'title': report.title} return self.jira_client.add_simple_link(jira, object=link) def add_jira_attachment(self, jira, attachment, filename): """Add H1 Attachment in Jira :param jira: Jira object that has attachments :param attachment: hacker one attachment object content :param filename: attachment file name :return: return """ return self.jira_client.add_attachment(issue=jira.id, attachment=attachment, filename=filename) def create_comments(self, jira, comment): return self.jira_client.add_comment(jira, comment)