Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/tools/modules/file_pull_requests.rb
Views: 11766
#!/usr/bin/env ruby12##3# This module requires Metasploit: https://metasploit.com/download4# Current source: https://github.com/rapid7/metasploit-framework5##67#8# This tool allows you to find all the pull requests for a particular file in the Metasploit9# repository. It does not include commit history from SVN.10#11# Author: sinn3r12#1314require 'net/http'15require 'optparse'1617begin18require 'octokit'19require 'nokogiri'20rescue LoadError => e21gem = e.message.split.last22abort "#{gem} not installed: please run `gem install #{gem}'"23end2425module FilePullRequestCollector2627class Exception < RuntimeError; end2829class PullRequestFinder3031attr_accessor :git_client32attr_accessor :repository33attr_accessor :branch34attr_accessor :owner35attr_accessor :git_access_token3637# Initializes parameters.38#39# @param api_key [String] Personal access token from Github.40# @return [void]41def initialize(api_key)42self.owner = 'rapid7'43self.repository = "#{owner}/metasploit-framework"44self.branch = 'master'45self.git_access_token = api_key46self.git_client = Octokit::Client.new(access_token: git_access_token)47end4849# Returns the commit history of a file.50#51# @param path [String] A file path in the Metasploit repository.52# @return [Array<Sawyer::Resource>] An array of commits.53# @raise [FilePullRequestCollector::Exception] No commits found. Probably the file path is wrong.54def get_commits_from_file(path)55commits = git_client.commits(repository, branch, path: path)56if commits.empty?57# Possibly the path is wrong.58raise FilePullRequestCollector::Exception, 'No commits found.'59end6061commits62end6364# Returns the author of a commit.65#66# @param commit [Sawyer::Resource] Commit.67# @return [String]68def get_author(commit)69if commit.author70return commit.author[:login].to_s71end7273''74end7576# Checks if a author should be ignored or not.77#78# @param commit [Sawyer::Resource] Commit.79# @return [TrueClass] Author should be ignored80# @return [FalseClass] Author should not be ignored.81def is_author_blacklisted?(commit)82['tabassassin'].include?(get_author(commit))83end8485# Returns all found pull requests.86#87# @param commits [Array<Sawyer::Resource>] Commits88# @return [Hash]89def get_pull_requests_from_commits(commits)90pull_requests = {}9192commits.each do |commit|93next if is_author_blacklisted?(commit)9495pr = get_pull_request_from_commit(commit)96unless pr.empty?97pull_requests[pr[:number]] = pr98end99end100101pull_requests102end103104# Returns the found pull request for a commit.105#106# @param commit [Sawyer::Resource] Commit107# @return [Hash]108def get_pull_request_from_commit(commit)109sha = commit.sha110url = URI.parse("https://github.com/#{repository}/branch_commits/#{sha}")111cli = Net::HTTP.new(url.host, url.port)112cli.use_ssl = true113req = Net::HTTP::Get.new(url.request_uri)114res = cli.request(req)115n = Nokogiri::HTML(res.body)116found_pr_link = n.at('li[@class="pull-request"]//a')117118# If there is no PR associated with this commit, it's probably from the SVN days.119return {} unless found_pr_link120121href = found_pr_link.attributes['href'].text122title = found_pr_link.attributes['title'].text123124# Filter out all the pull requests that do not belong to rapid7.125# If this happens, it's probably because the PR was submitted to somebody's fork.126return {} unless /^\/#{owner}\// === href127128{ number: href.scan(/\d+$/).flatten.first, title: title }129end130end131132class Client133134attr_accessor :finder135136# Initializes parameters.137#138# @param api_key [String]139# @return [void]140def initialize(api_key)141self.finder = PullRequestFinder.new(api_key)142end143144# Prints all the found PRs for a file.145#146# @param file_name [String] The file to look up.147# @return [void]148def search(file_name)149commits = finder.get_commits_from_file(file_name)150pull_requests = finder.get_pull_requests_from_commits(commits)151puts "Pull request(s) associated with #{file_name}"152pull_requests.each_pair do |number, pr|153puts "##{number} - #{pr[:title]}"154end155end156end157158class OptsParser159160def self.banner161%Q|162This tool collects all the pull requests submitted to rapid7/metasploit-framework for a163particular file. It does not include history from SVN (what Metasploit used to use164before Git).165166Usage: #{__FILE__} [options]167168Usage Example:169#{__FILE__} -k KEY -f modules/exploits/windows/browser/ms13_069_caret.rb170or171export GITHUB_OAUTH_TOKEN=KEY172#{__FILE__} -f modules/exploits/windows/browser/ms13_069_caret.rb173174How to obtain an API key (access token):1751. Go to github.com.1762. Go to Settings under your profile.1773. Click on Personal Access Tokens1784. Click on Generate new token1795. Follow the steps on the screen to complete the process.180181|182end183184def self.parse(args)185options = {}186187opts = OptionParser.new do |opts|188opts.banner = banner.strip.gsub(/^[[:blank:]]{4}/, '')189190opts.separator ""191opts.separator "Specific options:"192193opts.on("-k", "-k <key>", "Github Access Token") do |v|194options[:api_key] = v195end196197opts.on("-f", "--file <name>", "File name") do |v|198options[:file] = v199end200201opts.separator ""202opts.separator "Common options:"203204opts.on_tail("-h", "--help", "Show this message") do205puts opts206exit207end208end209210begin211opts.parse!(args)212rescue OptionParser::InvalidOption213abort "Invalid option, try -h for usage"214end215216if options.empty?217abort "No options specified, try -h for usage"218end219220options221end222end223224end225226if __FILE__ == $PROGRAM_NAME227begin228opts = FilePullRequestCollector::OptsParser.parse(ARGV)229rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e230abort "#{e.message} (please see -h)"231end232233if !opts.has_key?(:api_key)234if !ENV.has_key?('GITHUB_OAUTH_TOKEN')235abort <<EOF236A Github Access Token must be specified to use this tool237Please set GITHUB_OAUTH_TOKEN or specify the -k option238EOF239else240opts[:api_key] = ENV['GITHUB_OAUTH_TOKEN']241end242end243244begin245cli = FilePullRequestCollector::Client.new(opts[:api_key])246cli.search(opts[:file])247rescue FilePullRequestCollector::Exception => e248abort e.message249rescue Interrupt250$stdout.puts251$stdout.puts "Good bye"252end253end254255256