CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/tools/dev/find_release_notes.rb
Views: 1904
1
#!/usr/bin/env ruby
2
3
##
4
# This module requires Metasploit: https://metasploit.com/download
5
# Current source: https://github.com/rapid7/metasploit-framework
6
##
7
8
require 'net/http'
9
require 'nokogiri'
10
require 'thread'
11
12
module ReleaseNotesFinder
13
# This finds the release notes information based on either:
14
# 1. A PR number. In release notes, PR numbers are for bug fixes and notable changes.
15
# 2. A module short name. For example: ms08_067_netapi
16
class Client
17
attr_accessor :release_notes
18
19
RELEASE_NOTES_PAGE = 'https://community.rapid7.com/docs/DOC-2918'.freeze
20
21
def initialize
22
init_release_notes
23
@mutex = Mutex.new
24
end
25
26
def add_release_notes_entry(row)
27
td = row.search('td')
28
release_notes_link = td[0] && td[0].at('a') ? td[0].at('a').attributes['href'].value : ''
29
release_notes_num = td[0] && td[0].at('a') ? td[0].at('a').text.scan(/\d{10}/).flatten.first || '' : ''
30
highlights = td[1] ? (td[1].search('span') || []).map { |e| e.text } * " " : ''
31
update_link = td[2] && td[2].at('a') ? td[2].at('a').attributes['href'].value : ''
32
33
@release_notes << {
34
release_notes_link: release_notes_link,
35
release_notes_num: release_notes_num,
36
highlights: highlights,
37
update_link: update_link,
38
pull_requests: [],
39
new_modules: []
40
}
41
end
42
43
def init_release_notes
44
self.release_notes = []
45
46
html = send_http_request(RELEASE_NOTES_PAGE)
47
table_rows_pattern = 'div[@id="jive-body-main"]//div//section//div//div[@class="j-rte-table"]//table//tbody//tr'
48
rows = html.search(table_rows_pattern)
49
rows.each do |row|
50
add_release_notes_entry(row)
51
end
52
end
53
54
def update_pr_list(n, text)
55
pr_num, desc = text.scan(/#(\d+).\x20*(.+)/).flatten
56
return unless pr_num
57
n[:pull_requests] << { id: pr_num, description: desc }
58
end
59
60
def update_module_list(n, li)
61
li.search('a').each do |a|
62
next if a.attributes['href'].nil?
63
n[:new_modules] << { link: a.attributes['href'].value }
64
end
65
end
66
67
def update_release_notes_entry(n)
68
html = send_http_request(n[:release_notes_link])
69
pattern = '//div[@class="jive-rendered-content"]//ul//li'
70
html.search(pattern).each do |li|
71
@mutex.synchronize do
72
update_pr_list(n, li.text)
73
update_module_list(n, li)
74
end
75
end
76
end
77
78
def get_release_notes(input)
79
release_notes.each do |n|
80
if n[:pull_requests].empty?
81
update_release_notes_entry(n)
82
end
83
84
input_type = guess_input_type(input)
85
86
case input_type
87
when :pr
88
m = get_release_notes_from_pr(n, input)
89
when :module_name
90
m = get_release_notes_from_module_name(n, input)
91
end
92
93
return m if m
94
end
95
96
nil
97
end
98
99
def guess_input_type(input)
100
input =~ /^\d+/ ? :pr : :module_name
101
end
102
103
def get_release_notes_from_module_name(n, input)
104
n[:new_modules].each do |m|
105
return n if m[:link] && m[:link].include?(input)
106
end
107
108
nil
109
end
110
111
def get_release_notes_from_pr(n, pr)
112
n[:pull_requests].each do |p|
113
return n if p[:id] && pr == p[:id]
114
end
115
116
nil
117
end
118
119
def send_http_request(uri)
120
url = URI.parse(uri)
121
cli = Net::HTTP.new(url.host, url.port)
122
cli.use_ssl = true
123
req = Net::HTTP::Get.new(url.request_uri)
124
res = cli.request(req)
125
Nokogiri::HTML(res.body)
126
end
127
end
128
end
129
130
def main
131
inputs = []
132
133
ARGV.length.times { inputs << ARGV.shift }
134
puts "[*] Enumerating release notes..."
135
cli = ReleaseNotesFinder::Client.new
136
puts "[*] Finding release notes for items: #{inputs * ', '}"
137
threads = []
138
begin
139
inputs.each do |input|
140
t = Thread.new do
141
n = cli.get_release_notes(input)
142
puts "\n"
143
144
if n
145
puts "[*] Found release notes for: #{input}"
146
puts "Release Notes Number: #{n[:release_notes_num]}"
147
puts "Release Notes Link: #{n[:release_notes_link] || 'N/A'}"
148
puts "Update Link: #{n[:update_link] || 'N/A'}"
149
puts "Highlights:\n#{n[:highlights]}"
150
else
151
puts "[*] Unable to find release notes for: #{input}"
152
end
153
end
154
threads << t
155
end
156
threads.each { |t| t.join }
157
ensure
158
threads.each { |t| t.kill }
159
end
160
end
161
162
if __FILE__ == $PROGRAM_NAME
163
main
164
end
165
166