All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.shib.lib.jirabugsbuddy.VulnerabilityIssueHandler Maven / Gradle / Ivy

There is a newer version: 0.1.4
Show newest version
package me.shib.lib.jirabugsbuddy;

import me.shib.java.lib.jiraclient.*;
import me.shib.lib.jirabugsbuddy.types.JiraConfig;
import me.shib.lib.jirabugsbuddy.types.Vulnerability;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class VulnerabilityIssueHandler {

    private boolean errorFound;
    private JiraConfig jiraConfig;
    private String repo;
    private JiraClient jira;
    private String project;
    private int created;
    private int updated;
    private boolean allowWrite;

    public VulnerabilityIssueHandler() throws IOException, JiraException {
        this.jiraConfig = JiraConfig.getConfig();
        this.jira = JiraConfig.getJiraClient();
        this.project = jiraConfig.getProjectKey();
        this.repo = JiraConfig.getGitRepo();
        this.errorFound = false;
        this.created = 0;
        this.updated = 0;
        this.allowWrite = true;
    }

    public void disableWrite() {
        System.out.println("Disabling write operations in JIRA");
        this.allowWrite = false;
    }

    private void createIssueForVulnerability(Vulnerability vulnerability) throws JiraException {
        String summary = vulnerability.getJiraSummary();
        List jiraLabels = vulnerability.getJiraLabels();
        jiraLabels.add(jiraConfig.getAutomationLabel());
        Issue.FluentCreate fluentCreate = jira.createIssue(project, jiraConfig.getIssueType())
                .field(Field.SUMMARY, summary)
                .field(Field.DESCRIPTION, vulnerability.getJiraDescription())
                .field(Field.LABELS, jiraLabels)
                .field(Field.PRIORITY, vulnerability.getPriority());
        if (jiraConfig.getUsers().getReporter() != null) {
            fluentCreate.field(Field.REPORTER, jiraConfig.getUsers().getReporter());
        }
        if (jiraConfig.getUsers().getAssignee() != null) {
            fluentCreate.field(Field.ASSIGNEE, jiraConfig.getUsers().getAssignee());
        }
        for (String key : jiraConfig.getCustomFields().keySet()) {
            fluentCreate.field(key, Field.valueById(jiraConfig.getCustomFields().get(key)));
        }
        Issue issue = fluentCreate.execute();
        for (String watcher : jiraConfig.getUsers().getWatchers()) {
            issue.addWatcher(watcher);
        }
        System.out.println("Created new issue: " + issue.getKey() + " - " + issue.getSummary() + " with priority "
                + issue.getPriority().getName());
        created++;
    }

    private void updateIssueForVulnerability(Issue issue, Vulnerability vulnerability) throws JiraException {
        if (jiraConfig.isIssueIgnorable(issue)) {
            System.out.println("Ignoring the issue: " + issue.getKey());
            return;
        }
        boolean issueUpdated = false;
        Issue.FluentUpdate fluentUpdate = issue.update();
        if (jiraConfig.isSummaryUpdateAllowed() && !issue.getSummary().contentEquals(vulnerability.getJiraSummary())) {
            fluentUpdate.field(Field.SUMMARY, vulnerability.getJiraSummary());
            issueUpdated = true;
        }
        if (jiraConfig.isDescriptionUpdateAllowed() && !issue.getDescription().contentEquals(vulnerability.getJiraDescription())) {
            fluentUpdate.field(Field.DESCRIPTION, vulnerability.getJiraDescription());
            issueUpdated = true;
        }

        StringBuilder comment = new StringBuilder();
        if (jiraConfig.isReprioritizeAllowed() || jiraConfig.isDeprioritizeAllowed()) {
            int issuePriority;
            try {
                issuePriority = jiraConfig.getPriorities().get(issue.getPriority().getName());
            } catch (Exception e) {
                issuePriority = -1;
            }
            int vulnerabilityPriority = jiraConfig.getPriorities().get(vulnerability.getPriority());
            if (((issuePriority < vulnerabilityPriority) && (jiraConfig.isReprioritizeAllowed())) || ((issuePriority > vulnerabilityPriority) && (jiraConfig.isDeprioritizeAllowed()))) {
                fluentUpdate.field(Field.PRIORITY, vulnerability.getPriority());
                comment.append("Reprioritizing to *")
                        .append(vulnerability.getPriority())
                        .append("* based on actual priority.");
                issueUpdated = true;
            }
        }

        if (issueUpdated) {
            fluentUpdate.execute();
            if (!comment.toString().isEmpty()) {
                issue.addComment(comment.toString());
            }
            updated++;
        }

        if (jiraConfig.isOpenAllowedForStatus(issue.getStatus().getName())) {
            reopenIssue(issue);
        } else if (issueUpdated) {
            System.out.println("Updated the issue: " + issue.getKey() + " - "
                    + issue.getSummary());
        } else {
            System.out.println("Issue up-to date: " + issue.getKey() + " - "
                    + issue.getSummary());
        }
    }

    private void processVulnerability(Vulnerability vulnerability) throws Exception {
        StringBuilder jql = new StringBuilder();
        jql.append("project = ").append(project);
        jql.append(" AND issuetype = \"").append(jiraConfig.getIssueType()).append("\"");
        Set jiraSearchLabels = new HashSet<>(vulnerability.getJiraKeyLabels());
        jiraSearchLabels.add(jiraConfig.getAutomationLabel());
        jiraSearchLabels.add(repo);
        for (String label : jiraSearchLabels) {
            jql.append(" AND labels = ").append("\"").append(label).append("\"");
        }
        List issues = jira.searchIssues(jql.toString(), JiraConfig.maxSearchResult).issues;
        if (issues.size() == 0) {
            if (allowWrite) {
                createIssueForVulnerability(vulnerability);
            } else {
                System.out.println("Creating issue DUMMY-CREATED-" + created + ": " + vulnerability.getJiraSummary());
            }
        } else {
            if (issues.size() == 1) {
                if (allowWrite) {
                    updateIssueForVulnerability(issues.get(0), vulnerability);
                } else {
                    System.out.println("Skipping Update of " + issues.get(0).getKey() + ": " + issues.get(0).getSummary());
                }
            } else {
                throw new Exception("More than one issue listed:\n"
                        + "Labels: " + Arrays.toString(jiraSearchLabels.toArray()) + "\n"
                        + "Issues: " + Arrays.toString(issues.toArray()));
            }
        }
    }

    private boolean isVulnerabilityExists(Issue issue, List vulnerabilities) {
        for (Vulnerability vulnerability : vulnerabilities) {
            if (issue.getLabels().containsAll(vulnerability.getJiraKeyLabels())) {
                return true;
            }
        }
        return false;
    }

    private String getStatusTransition(Issue issue, String status) throws JiraException {
        List transitions = issue.getTransitions();
        for (Transition transition : transitions) {
            if (transition.getToStatus().getName().equalsIgnoreCase(status)) {
                return transition.getName();
            }
        }
        return null;
    }

    private boolean transitionIssue(List transitions, Issue issue, String verb) {
        try {
            if (transitions.size() > 1) {
                StringBuilder consoleLog = new StringBuilder();
                consoleLog.append(" ").append(verb).append(" the issue ")
                        .append(issue.getKey()).append(": ").append(transitions.get(0));
                for (int i = 1; i < transitions.size(); i++) {
                    consoleLog.append(" -> ").append(transitions.get(i));
                    issue.transition().execute(getStatusTransition(issue, transitions.get(i)));
                }
                System.out.print(consoleLog.toString());
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            errorFound = true;
        }
        return false;
    }

    private void reopenIssue(Issue issue) throws JiraException {
        System.out.print("Issue: " + issue.getKey() + " is resolved, but not actually fixed. ");
        boolean transitioned = false;
        if (jiraConfig.toOpen().isTansitionAllowed()) {
            List transitions = jiraConfig.getTransitionsToOpen(issue.getStatus().getName());
            transitioned = transitionIssue(transitions, issue, "Reopening");
            if (!transitioned) {
                System.out.println(" No path defined to Open the issue from \"" + issue.getStatus().getName() + "\" state.");
            }
        }
        if (jiraConfig.toOpen().isCommentingAllowed(issue, JiraConfig.issueNotFixedComment)) {
            StringBuilder comment = new StringBuilder();
            comment.append(JiraConfig.issueNotFixedComment);
            if (!transitioned) {
                comment.append(JiraConfig.issueReopenComment);
            }
            if (issue.getAssignee() != null && !jira.getSelf().equalsIgnoreCase(issue.getAssignee().getName())) {
                comment.append("\n[~").append(issue.getAssignee().getName()).append("]");
            }
            if (issue.getReporter() != null && !jira.getSelf().equalsIgnoreCase(issue.getReporter().getName())) {
                comment.append("\n[~").append(issue.getReporter().getName()).append("]");
            }
            issue.addComment(comment.toString());
        }
        System.out.print("\n");
    }

    private void closeIssue(Issue issue) throws JiraException {
        if (jiraConfig.isIssueIgnorable(issue)) {
            System.out.println("Ignoring the issue: " + issue.getKey());
            return;
        }
        System.out.print("Issue: " + issue.getKey() + " has been fixed.");
        boolean transitioned = false;
        if (jiraConfig.toClose().isTansitionAllowed() && jiraConfig.isClosingTransitionAllowedForStatus(issue.getStatus().getName())) {
            List transitions = jiraConfig.getTransitionsToClose(issue.getStatus().getName());
            transitioned = transitionIssue(transitions, issue, "Closing");
            if (!transitioned) {
                System.out.println(" No path defined to Close the issue from \"" + issue.getStatus().getName() + "\" state.");
            }
        }
        if (jiraConfig.toClose().isCommentingAllowed(issue, JiraConfig.issueFixedComment)) {
            StringBuilder comment = new StringBuilder();
            comment.append(JiraConfig.issueFixedComment);
            if (!transitioned) {
                comment.append(JiraConfig.issueCloseComment);
            }
            if (issue.getAssignee() != null && !jira.getSelf().equalsIgnoreCase(issue.getAssignee().getName())) {
                comment.append("\n[~").append(issue.getAssignee().getName()).append("]");
            }
            if (issue.getReporter() != null && !jira.getSelf().equalsIgnoreCase(issue.getReporter().getName())) {
                comment.append("\n[~").append(issue.getReporter().getName()).append("]");
            }
            issue.addComment(comment.toString());
        }
        System.out.print("\n");
    }

    public boolean processBugs(List vulnerabilities) throws Exception {
        System.out.println("Vulnerabilities found: " + vulnerabilities.size());
        for (Vulnerability vulnerability : vulnerabilities) {
            processVulnerability(vulnerability);
        }
        if (jiraConfig.isClosingAllowed()) {
            StringBuilder jql = new StringBuilder();
            jql.append("project = ").append(project)
                    .append(" AND labels = ").append("\"").append(jiraConfig.getAutomationLabel()).append("\"")
                    .append(" AND labels = ").append("\"").append(repo).append("\"")
                    .append(" AND issuetype = \"").append(jiraConfig.getIssueType()).append("\"");
            for (String status : jiraConfig.getCloseStatuses()) {
                jql.append(" AND status != \"").append(status).append("\"");
            }
            List issues = jira.searchIssues(jql.toString(), JiraConfig.maxSearchResult).issues;
            for (Issue issue : issues) {
                if (!isVulnerabilityExists(issue, vulnerabilities)) {
                    if (allowWrite) {
                        closeIssue(issue);
                    } else {
                        System.out.println("Skipping attempt to close: " + issue.getKey() + ": " + issue.getSummary());
                    }
                    updated++;
                }
            }
        }
        System.out.println("Issues created: " + created);
        System.out.println("Issues updated: " + updated);
        return !errorFound;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy