#!/usr/bin/env ruby # frozen_string_literal: false require 'cgi' require 'optparse' require 'pwn' require 'yaml' opts = {} OptionParser.new do |options| options.banner = "USAGE: #{$PROGRAM_NAME} [opts] " options.on('-cCONFIG', '--config=CONFG', '') do |g| opts[:config] = g end options.on('-pNAME', '--parent-group=NAME', '') do |p| opts[:parent_group_name] = p end options.on('-sFILE', '--scan=FILE', '') do |f| opts[:target_file] = f end options.on('-rPATH', '--report=PATH', '') do |r| opts[:report_path] = r end options.on('-R', '--report-only', '') do |o| opts[:report_only] = o end options.on('-tTYPE', '--report-type=TYPE', '') do |t| opts[:report_type] = t end options.on('-vVERSION', '--version=VERSION', '') do |v| opts[:version] = v end end.parse! if opts.empty? puts `#{$PROGRAM_NAME} --help` exit 1 end begin pwn_provider = 'ruby-gem' pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' } config = opts[:config] raise "ERROR: BDBA YAML Config File Not Found: #{config}" unless File.exist?(config) yaml_config = YAML.load_file(config, symbolize_names: true) token = yaml_config[:token] raise "ERROR: BDBA Token Not Found: #{token}" if token.nil? parent_group_name = opts[:parent_group_name] raise "ERROR: BDBA Parent Group Name Not Provided: #{parent_group_name}" if parent_group_name.nil? target_file = opts[:target_file] raise "ERROR: BDBA Target File Not Found: #{target_file}" unless File.exist?(target_file) report_path = opts[:report_path] raise "ERROR: BDBA Report Path Not Provided: #{report_path}" if report_path.nil? report_only = opts[:report_only] ||= false report_type_str = opts[:report_type] ||= 'csv_vulns' report_type = report_type_str.to_s.to_sym version = opts[:version] groups_resp = PWN::Plugins::BlackDuckBinaryAnalysis.get_groups( token: token ) parent_arr = groups_resp[:groups].select { |g| g[:name] == parent_group_name } raise "ERROR: BDBA Parent Group Not Found: #{parent_group_name}" if parent_arr.nil? sorted_parent_arr = parent_arr.sort_by { |g| g[:id] } parent_id = sorted_parent_arr.last[:id] unless report_only puts "Uploading/Scanning: #{target_file}" PWN::Plugins::BlackDuckBinaryAnalysis.upload_file( token: token, file: target_file, group_id: parent_id, version: version ) end scan_progress_resp = {} scan_progress_busy_duration = 0 loop do scan_progress_resp = PWN::Plugins::BlackDuckBinaryAnalysis.get_apps_by_group( token: token, group_id: parent_id ) break if scan_progress_resp[:products].none? { |p| p[:status] == 'B' } || report_only # Cancel queued scan if it's been queued for more than 90 minutes if scan_progress_busy_duration > 5_400 scan_progress_resp[:products].select { |p| p[:status] == 'B' }.each do |p| puts "Abort Queued Scan: #{p[:name]}" PWN::Plugins::BlackDuckBinaryAnalysis.abort_product_scan( token: token, product_id: p[:product_id] ) end raise "ERROR: BDBA Scan Queued for More than 90 Minutes: #{target_file}" end 10.times do print '.' sleep 1 end scan_progress_busy_duration += 10 end product_id = scan_progress_resp[:products].find { |p| p[:name] == CGI.escape(File.basename(target_file)) }[:product_id] scan_report_resp = PWN::Plugins::BlackDuckBinaryAnalysis.generate_product_report( token: token, product_id: product_id, type: report_type, output_path: report_path ) puts "\nReport Saved to: #{report_path}" rescue SystemExit, Interrupt puts "\nGoodbye." rescue StandardError => e raise e end