Пример #1
0
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)
Пример #2
0
def main():
    context = utils.collect_context()
    tmpl_host_summary = 'Check_MK: $HOSTNAME$ - $HOSTSHORTSTATE$'
    tmpl_service_summary = 'Check_MK: $HOSTNAME$/$SERVICEDESC$ $SERVICESHORTSTATE$'
    tmpl_label = 'monitoring'

    for necessary in [
            'PARAMETER_URL', 'PARAMETER_USERNAME', 'PARAMETER_PASSWORD',
            'PARAMETER_HOST_CUSTOMID', 'PARAMETER_SERVICE_CUSTOMID'
    ]:
        if necessary not in context:
            sys.stderr.write("%s not set" % necessary)
            return 2

    if "PARAMETER_IGNORE_SSL" in context:
        sys.stdout.write(
            "Unverified HTTPS request warnings are ignored. Use with caution.\n"
        )
        jira = JIRA(server=context['PARAMETER_URL'],
                    basic_auth=(context['PARAMETER_USERNAME'],
                                context['PARAMETER_PASSWORD']),
                    options={'verify': False})
    else:
        jira = JIRA(server=context['PARAMETER_URL'],
                    basic_auth=(context['PARAMETER_USERNAME'],
                                context['PARAMETER_PASSWORD']))

    if context['WHAT'] == 'HOST':
        summary = context.get('PARAMETER_HOST_SUMMARY') or tmpl_host_summary
        svc_desc = context['HOSTOUTPUT']
        custom_field = int(context['PARAMETER_HOST_CUSTOMID'])
        custom_field_value = int(context['HOSTPROBLEMID'])
    else:
        summary = context.get(
            'PARAMETER_SERVICE_SUMMARY') or tmpl_service_summary
        svc_desc = context['SERVICEOUTPUT']
        custom_field = int(context['PARAMETER_SERVICE_CUSTOMID'])
        custom_field_value = int(context['SERVICEPROBLEMID'])

    context['SUBJECT'] = utils.substitute_context(summary, context)
    label = context.get('PARAMETER_LABEL') or tmpl_label
    newissue = {
        u'labels': [label],
        u'summary': context['SUBJECT'],
        u'description': svc_desc,
    }

    if 'PARAMETER_PROJECT' in context:
        newissue[u'project'] = {u'id': context['PARAMETER_PROJECT']}
    if 'CONTACT_JIRAPROJECT' in context:
        newissue[u'project'] = {u'id': context['CONTACT_JIRAPROJECT']}
    if 'PARAMETER_ISSUETYPE' in context:
        newissue[u'issuetype'] = {u'id': context['PARAMETER_ISSUETYPE']}
    if 'CONTACT_JIRAISSUETYPE' in context:
        newissue[u'issuetype'] = {u'id': context['CONTACT_JIRAISSUETYPE']}
    if 'PARAMETER_PRIORITY' in context:
        newissue[u'priority'] = {u'id': context['PARAMETER_PRIORITY']}
    if 'CONTACT_JIRAPRIORITY' in context:
        newissue[u'priority'] = {u'id': context['CONTACT_JIRAPRIORITY']}
    if 'project' not in newissue:
        sys.stderr.write("No JIRA project ID set, discarding notification")
        return 2
    if 'issuetype' not in newissue:
        sys.stderr.write("No JIRA issue type ID set")
        return 2

    try:
        custom_field_exists = jira.search_issues(
            "cf[%d]=%d" % (custom_field, custom_field_value))
    except JIRAError as err:
        sys.stderr.write(
            'Unable to query custom field search, JIRA response code %s, %s' %
            (err.status_code, err.text))
        return 2

    if not custom_field_exists:
        newissue[u'customfield_%d' % custom_field] = custom_field_value

    if context['NOTIFICATIONTYPE'] == 'PROBLEM':
        try:
            issue = jira.create_issue(fields=newissue)
        except JIRAError as err:
            sys.stderr.write(
                'Unable to create issue, JIRA response code %s, %s' %
                (err.status_code, err.text))
            return 2
        sys.stdout.write('Created %s\n' % issue.permalink())
        if 'PARAMETER_MONITORING' in context:
            if context['PARAMETER_MONITORING'].endswith('/'):
                # remove trailing slash
                context['PARAMETER_MONITORING'] = context[
                    'PARAMETER_MONITORING'][:-1]
            if context['WHAT'] == 'SERVICE':
                url = context['PARAMETER_MONITORING'] + context['SERVICEURL']
            else:
                url = context['PARAMETER_MONITORING'] + context['HOSTURL']
            try:
                rl = jira.add_simple_link(issue, {
                    'url': url,
                    'title': 'Monitoring'
                })
            except JIRAError as err:
                sys.stderr.write(
                    'Unable to create link in issue, JIRA response code %s, %s\n'
                    % (err.status_code, err.text))
                return 2
            sys.stdout.write('Created JIRA simple link: %s' % rl)

    if context['NOTIFICATIONTYPE'] == 'RECOVERY' and custom_field_exists:
        if "PARAMETER_RESOLUTION" not in context:
            sys.stderr.write(
                "Ticket resolution not enabled in wato rule. Don't send a resolution to jira\n"
            )
            return 0
        else:
            resolution = None
            if 'PARAMETER_RESOLUTION' in context:
                resolution = context['PARAMETER_RESOLUTION']
            if 'CONTACT_JIRARESOLUTION' in context:
                resolution = context['CONTACT_JIRARESOLUTION']
            if resolution is None:
                sys.stderr.write("No JIRA resolution ID set")
                return 2
        for issue in custom_field_exists:
            try:
                jira.transition_issue(issue,
                                      resolution,
                                      comment=newissue['description'])
                sys.stdout.write('Resolved %s' % issue.permalink())
            except JIRAError as err:
                sys.stderr.write(
                    'Unable to resolve %s, JIRA response code %s, %s' %
                    (issue.permalink(), err.status_code, err.text))
                return 2