#!/usr/bin/env ruby require 'getoptlong' require 'json' require 'terminal-table' require 'justify' # require 'codesake-commons' require 'dawnscanner' APPNAME = File.basename($0) LIST_KNOWN_FRAMEWORK = %w(rails sinatra padrino) VALID_OUTPUT_FORMAT = %w(console json csv html) # $logger = Codesake::Commons::Logging.instance require 'logger' $logger = Logger.new(STDOUT) $logger.datetime_format = '%Y-%m-%d %H:%M:%S' opts = GetoptLong.new( # report formatting options [ '--ascii-tabular-report', '-a', GetoptLong::NO_ARGUMENT], [ '--json', '-j', GetoptLong::NO_ARGUMENT], [ '--html', '-H', GetoptLong::NO_ARGUMENT], # MVC forcing [ '--rails', '-r', GetoptLong::NO_ARGUMENT], [ '--sinatra', '-s', GetoptLong::NO_ARGUMENT], [ '--padrino', '-p', GetoptLong::NO_ARGUMENT], [ '--gem-lock', '-G', GetoptLong::REQUIRED_ARGUMENT], [ '--count-only', '-C', GetoptLong::NO_ARGUMENT], [ '--exit-on-warn', '-z', GetoptLong::NO_ARGUMENT], # Disable checks by family type [ '--disable-cve-bulletins', GetoptLong::NO_ARGUMENT], [ '--disable-code-quality', GetoptLong::NO_ARGUMENT], [ '--disable-code-style', GetoptLong::NO_ARGUMENT], [ '--disable-owasp-ror-cheatsheet', GetoptLong::NO_ARGUMENT], [ '--disable-owasp-top-10', GetoptLong::NO_ARGUMENT], # Search knowledge base [ '--search-knowledge-base', '-S', GetoptLong::REQUIRED_ARGUMENT], # List stuff [ '--list-knowledge-base', GetoptLong::NO_ARGUMENT], [ '--list-known-framework', GetoptLong::NO_ARGUMENT], [ '--list-known-families', GetoptLong::NO_ARGUMENT], # please save output to file [ '--file', '-F', GetoptLong::REQUIRED_ARGUMENT], # specify an alternate config file [ '--config-file', '-c', GetoptLong::REQUIRED_ARGUMENT], # service options [ '--verbose', '-V', GetoptLong::NO_ARGUMENT], [ '--debug', '-D', GetoptLong::NO_ARGUMENT], [ '--version', '-v', GetoptLong::NO_ARGUMENT], [ '--help', '-h', GetoptLong::NO_ARGUMENT] ) opts.quiet=true engine = nil options = Dawn::Core.read_conf(Dawn::Core.find_conf(true)) check = "" guess = {:name=>"", :version=>"", :connected_gems=>[]} begin opts.each do |opt, val| case opt when '--version' puts "#{Dawn::VERSION} [#{Dawn::CODENAME}]" Kernel.exit(0) when '--config-file' options = Dawn::Core.read_conf(val) when '--disable-cve-bulletins' options[:enabled_checks].delete(:cve_bulletin) when '--disable-code-quality' options[:enabled_checks].delete(:code_quality) when '--disable-code-style' options[:enabled_checks].delete(:code_style) when '--disable-owasp-ror-cheatsheet' options[:enabled_checks].delete(:owasp_ror_cheatsheet) when '--disable-owasp-top-10' options[:enabled_checks].delete(:owasp_top_10_1) options[:enabled_checks].delete(:owasp_top_10_2) options[:enabled_checks].delete(:owasp_top_10_3) options[:enabled_checks].delete(:owasp_top_10_4) options[:enabled_checks].delete(:owasp_top_10_5) options[:enabled_checks].delete(:owasp_top_10_6) options[:enabled_checks].delete(:owasp_top_10_7) options[:enabled_checks].delete(:owasp_top_10_8) options[:enabled_checks].delete(:owasp_top_10_9) options[:enabled_checks].delete(:owasp_top_10_10) when '--list-known-families' printf "Dawn supports following check families:\n\n" puts Dawn::Kb::BasicCheck.families Kernel.exit(0) when '--json' options[:output] = "json" when '--ascii-tabular-report' options[:output] = "tabular" when '--html' options[:output] = "html" when '--rails' options[:mvc]=:rails when '--sinatra' options[:mvc]=:sinatra when '--padrino' options[:mvc]=:padrino when '--file' options[:filename] = val when '--gem-lock' options[:gemfile_scan] = true unless val.empty? options[:gemfile_name] = val guess = Dawn::Core.guess_mvc(val) end when '--verbose' options[:verbose]=true when '--count-only' options[:output] = "count" when '--debug' options[:debug] = true when '--exit-on-warn' options[:exit_on_warn] = true when '--search-knowledge-base' found = Dawn::KnowledgeBase.find(nil, val) puts "#{val} found in knowledgebase." if found puts "#{val} not found in knowledgebase" if ! found Kernel.exit(0) when '--list-knowledge-base' puts Dawn::Core.dump_knowledge_base(options[:verbose]) Kernel.exit(0) when '--list-known-framework' puts "Ruby MVC framework supported by #{APPNAME}:" LIST_KNOWN_FRAMEWORK.each do |mvc| puts "* #{mvc}" end Kernel.exit(0) when '--help' Kernel.exit(Dawn::Core.help) end end rescue GetoptLong::InvalidOption => e $logger.helo APPNAME, Dawn::VERSION $logger.error e.message Kernel.exit(Dawn::Core.help) end target=ARGV.shift $logger.helo APPNAME, Dawn::VERSION trap("INT") { $logger.die('[INTERRUPTED]') } $logger.die("missing target") if target.nil? && options[:gemfile_name].empty? $logger.die("invalid directory (#{target})") if options[:gemfile_name].empty? &&! Dawn::Core.is_good_target?(target) $logger.die("if scanning Gemfile.lock file you must not force target MVC using one from -r, -s or -p flag") if ! options[:mvc].empty? && options[:gemfile_scan] $logger.debug("security check enabled: #{options[:enabled_checks]}") if options[:debug] ## MVC auto detect. # Skipping MVC autodetect if it's already been done by guess_mvc when choosing Gemfile.lock scan unless options[:gemfile_scan] begin if options[:mvc].empty? engine = Dawn::Core.detect_mvc(target) $logger.debug("using #{engine.class.name} engine via autodect") if options[:debug] else engine = Dawn::Rails.new(target) if options[:mvc] == :rails engine = Dawn::Sinatra.new(target) if options[:mvc] == :sinatra engine = Dawn::Padrino.new(target) if options[:mvc] == :padrino end rescue ArgumentError => e $logger.die(e.message) end else engine = Dawn::GemfileLock.new(target, options[:gemfile_name], guess) # if options[:gemfile_scan] end $logger.die("ruby framework auto detect failed. Please force if rails, sinatra or padrino with -r, -s or -p flags") if engine.nil? if options[:exit_on_warn] Kernel.at_exit do if engine.count_vulnerabilities != 0 Kernel.exit(engine.count_vulnerabilities) end end end if options[:debug] $logger.warn "putting engine in debug mode" engine.debug = true end $logger.die "missing target framework option" if engine.nil? $logger.warn "this is a development Dawn version" if Dawn::RELEASE == "(development)" $logger.die "nothing to do on #{target}" if ! options[:gemfile_scan] && ! engine.can_apply? engine.load_knowledge_base(options[:enabled_checks]) ret = engine.apply_all if options[:output] == "count" puts (ret)? engine.vulnerabilities.count : "-1" unless options[:output] == "json" puts (ret)? {:status=>"OK", :vulnerabilities_count=>engine.count_vulnerabilities}.to_json : {:status=>"KO", :vulnerabilities_count=>-1}.to_json Kernel.exit(0) end Dawn::Reporter.new({:engine=>engine, :apply_all_code=>ret, :format=>options[:output].to_sym, :filename=>options[:filename]}).report $logger.bye