-
Notifications
You must be signed in to change notification settings - Fork 28
/
merge_issues.js
187 lines (171 loc) · 5.59 KB
/
merge_issues.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* eslint-disable no-unused-vars */
/* globals Session Hosts Issues Meteor */
function mergeIssues (titleRegex, minCVSS, maxCVSS, hostsRegex, newTitle, newCVSS, update) {
// Merges all issues identified by the regular expressions into a new or existing Issue
// provided by newTitle.
//
// Usage:
// mergeIssues(/Apache/i, 7, 10, /.*/, 'Apache 2.x servers are vulnerable to multiple high risk issues', 'max', false)
// mergeIssues(/Apache/i, 7, 10, /.*/, 'Apache 2.x servers are vulnerable to multiple high risk issues', 'max', true)
//
// titleRegex - regex to search titles
// minCVSS - minimum CVSS score to include
// maxCVSS - maximum CVSS score to include
// hostsRegex - host IPs to include in filter
// newTitle - title of the new Issue
// newCVSS - new CVSS score, or choose 'max' to pick the highest CVSS score of that group
// update - The update parameter determines whether it's a 'dry run' with output, or an actual merge. update = true will delete old entries
//
// Created by: Alex Lauerman and Tom Steele
// Requires client-side updates: false
// Do some light variable checking, you're still pretty much on your own
if (typeof titleRegex !== 'object') {
return console.log('Issue regex can not be a string, must be a object')
}
if (typeof newTitle !== 'string') {
return console.log('Invalid title')
}
if (typeof newCVSS !== 'string') {
return console.log('Invalid cvss. Variable must be a string')
}
var projectId = Session.get('projectId')
var issues = Issues.find({
'projectId': projectId,
'title': {
'$regex': titleRegex
},
'cvss': {
'$gte': minCVSS,
'$lte': maxCVSS
},
'hosts.ipv4': {
'$regex': hostsRegex
}
}).fetch()
if (issues.length < 1) {
return console.log('Did not find any issues with the given regex')
}
var highestCVSS = 0
// You can change the sort order here
// issues.sort(sortByHostCount)
// issues.sort(sortByTitle)
issues.sort(sortByCVSS)
issues.forEach(function (Issue) {
console.log('CVSS: ' + Issue.cvss + ' - Hosts: ' + Issue.hosts.length + ' - Title: ' + Issue.title)
if (Issue.cvss > highestCVSS) {
highestCVSS = Issue.cvss
}
})
console.log('Total found: ' + issues.length + ' Highest CVSS: ' + highestCVSS)
if (update) {
if (newCVSS === 'max') {
newCVSS = highestCVSS
}
// If the Issue given in newTitle already exists, then we push it onto the regex list so we can combine them
// Remove the existing Issue first
var existingIssue = Issues.findOne({
'projectId': projectId,
'title': newTitle
})
if (typeof existingIssue !== 'undefined') {
issues.push(existingIssue)
Meteor.call('removeIssue', projectId, existingIssue._id)
}
console.log('Going to merge ' + issues.length + ' issues')
var newDescription = ''
var newSolution = ''
var newEvidence = ''
var newNotes = []
var newReferences = []
var cves = []
var hostList = []
var newFiles = []
// Loop over each Issue and combine the data
issues.forEach(function (Issue) {
newDescription = newDescription + 'CVSS: ' + Issue.cvss + ' - Hosts: ' + Issue.hosts.length + ' - Title: ' + Issue.title + "\n"
newSolution = ''
newEvidence = ''
newReferences = newReferences.concat(Issue.references)
newNotes = newNotes.concat(Issue.notes)
cves = cves.concat(Issue.cves)
hostList = hostList.concat(Issue.hosts)
newFiles = newFiles.concat(Issue.files)
})
var newHostList = unique(hostList)
var newCVEs = unique(cves)
// Create the new Issue
Meteor.call('createIssue', projectId, newTitle, newCVSS, newDescription, newEvidence, newSolution, function (err, res) {
if (err) {
console.log('Error: could not create new Issue', err.message)
if (existingIssue) {
console.log('Looks like you lost', existingIssue.title)
}
} else {
addExistingContentToIssue(res)
}
})
return console.log('Complete')
}
function sortByHostCount (a, b) {
if (a.hosts.length > b.hosts.length) {
return -1
}
if (a.hosts.length < b.hosts.length) {
return 1
}
return 0
}
function sortByTitle (a, b) {
if (a.hosts.title > b.hosts.title) {
return -1
}
if (a.hosts.title < b.hosts.title) {
return 1
}
return 0
}
function sortByCVSS (a, b) {
if (a.cvss > b.cvss) {
return -1
}
if (a.cvss < b.cvss) {
return 1
}
return 0
}
// Adds notes, hosts, and cves to new vulnerablity
function addExistingContentToIssue (issueId) {
newNotes.forEach(function (note) {
Meteor.call('addIssueNote', projectId, issueId, note.title, note.content)
})
newHostList.forEach(function (host) {
Meteor.call('addHostToIssue', projectId, issueId, host.ipv4, host.port, host.protocol)
})
newCVEs.forEach(function (cve) {
Meteor.call('addCVE', projectId, issueId, cve)
})
newReferences.forEach(function (ref) {
Meteor.call('addReference', projectId, issueId, ref.link, ref.name)
})
removeIssues()
}
// Loop over all issues and remove them
function removeIssues () {
console.log('Removing Issues')
issues.forEach(function (Issue) {
Meteor.call('removeIssue', projectId, Issue._id)
})
}
function unique (arr) {
var hash = {}
var result = []
for (var i = 0, l = arr.length; i < l; ++i) {
var objString = JSON.stringify(arr[i])
if (!hash.hasOwnProperty(objString)) {
hash[objString] = true
result.push(arr[i])
}
}
return result
}
}