#!/usr/bin/env ruby require_relative '../lib/rule_engine' require 'json' require 'launchy' require 'optparse' require 'optparse/uri' require 'logger' require_relative '../lib/puppet-sec-lint/version' require_relative '../lib/visitors/configuration_visitor' require_relative '../lib/facades/configuration_file_facade' ConfigurationVisitor.GenerateIDs ConfigurationFileFacade.LoadConfigurations $logger = Logger.new(STDOUT) $logger.level = Logger::ERROR #get free port loop do $port = rand(3000..9999) break if (Socket.tcp('localhost', port, connect_timeout: 5) { false } rescue true) end conf_page_url = "http://localhost:#{$port}/configuration" options = {} @success = true def analyze_file(file_path) File.open(file_path, 'rb:UTF-8') do |f| puts "Analyzing the file #{File.basename(file_path)}...\n\n" code = f.read result = RuleEngine.analyzeDocument(code) result.each do |sin| puts sin.ToString @success = false end puts "\nFound #{result.length} vulnerabilities in the puppet code.\n" end end OptionParser.new do |opts| opts.banner = "Usage: puppet-sec-lint [file or directory] [options]" opts.on("-c", "--configurations", "Open the linter rules configurations page on a browser") do |v| options[:configurations] = v end opts.on("-p", "--port=PORT", "TCP Port open for socket communication with the language server (Default:5007)") do |port| options[:port] = port end opts.on("-v", "--verbose", "Verbose mode (shows all communications and other debug info)") do |v| options[:verbose] = v $logger.level = Logger::DEBUG end end.parse! puts '___ _ _ ___ ___ ____ ___ ____ ____ ____ _ _ ____ _ ___ _ _ _ _ _ _ ___ ____ ____ ' puts '|__] | | |__] |__] |___ | [__ |___ | | | |__/ | | \_/ | | |\ | | |___ |__/ ' puts '| |__| | | |___ | ___] |___ |___ |__| | \ | | | |___ | | \| | |___ | \ ' puts "\n" puts "Release v#{PuppetSecLint::VERSION} #{PuppetSecLint::AUTHOR} #{PuppetSecLint::YEAR}" puts "\n" STDOUT.flush if not ARGV[0].nil? if File.file?(ARGV[0].to_s) && File.extname(ARGV[0].to_s) == '.pp' analyze_file(ARGV[0].to_s) elsif File.directory?(ARGV[0].to_s) Dir.chdir(ARGV[0].to_s) files = Dir.glob("**/*.pp").map {|f| File.join(Dir.pwd,f) } files.each do |file_path| analyze_file(file_path) puts "\n" end else raise "#{ARGV[0].to_s} is neither a valid directory or puppet file" end end if ARGV[0].nil? || options[:configurations] linter_server = Thread.new { require_relative '../lib/servers/linter_server' LinterServer.start($port) } language_server = Thread.new { require_relative '../lib/servers/language_server' LanguageServer.start(options[:port]) } if options[:configurations] puts "\nLaunching configurations page at #{conf_page_url}...\n\n" Launchy.open(conf_page_url) else puts "\nLinter configurations page available at #{conf_page_url}\n\n" puts "-----------------------------------------------------------------------" STDOUT.flush end linter_server.join language_server.exit end exit(@success)