class Sectest

Public Class Methods

load_manifest(sectest_name) click to toggle source

Loads a manifest file depending on the command rubocop:disable Style/GuardClause

# File lib/norad_cli/cli/sectest.rb, line 35
def self.load_manifest(sectest_name)
  @@sectest_manifest = {}

  # Set defaults just in case no manifest.yml to overwrite
  @@sectest_manifest['registry'] = 'norad-registry.cisco.com:5000'
  @@sectest_manifest['version'] = 'latest'

  # Dynamically add options and description based on the needs of the sectest container
  if %w[build build:all build:image build:specs execute].include?(ARGV[1]) && sectest_name && !sectest_name.start_with?('-', '--')
    # Read in the program arguments
    if File.exist?("sectests/#{sectest_name}/manifest.yml")
      @@sectest_manifest = YAML.safe_load(File.read("sectests/#{sectest_name}/manifest.yml"))

      # Precautionary, remove all leading and trailing whitespace
      @@sectest_manifest['registry'].strip!
      @@sectest_manifest['version'].strip!
    else
      puts Rainbow("Error: #{sectest_name} sectest does not exist or it is missing sectests/#{sectest_name}/manifest.yml").red
      puts Rainbow('Exiting...').red
      exit(1)
    end
  end
end
new(*args) click to toggle source

rubocop:enable Style/GuardClause

Calls superclass method
# File lib/norad_cli/cli/sectest.rb, line 60
def initialize(*args)
  super

  # Check if the command is being run from the repository root (all commands must be)
  root_dir?
end
source_root() click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 29
def self.source_root
  File.join(File.dirname(File.expand_path(__FILE__)), '../templates/')
end

Public Instance Methods

build() click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 118
def build
  # Error check to ensure this is a plugin directory
  Dir.glob('sectests/*').select do |f|
    # Skip if the entry is not a directory
    next if !File.directory? f

    # Grab the name of the sectest
    sectest_name = f.split('/')[-1]

    # Load the manifest for the sectest
    Sectest.load_manifest(sectest_name)

    # Build all for the sectest
    send('build:all', sectest_name)
  end
end
dockerfile?(img_dir) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 308
def dockerfile?(img_dir)
  # Ensure the Dockerfile exists for the new tool
  File.file?("#{img_dir}/Dockerfile")
end
execute(sectest_name) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 222
def execute(sectest_name)
  # Warn users if debugging is enabled
  say('Warning: Debug enabled: containers must be removed manually', :yellow) && sleep(2) if options['debug']

  # Ensure the results server is built by building the images specs (code reuse)
  send('build:specs', sectest_name)

  # Build the sectest image if necessary
  send('build:image', sectest_name)

  # Allocate an instance of the sectest
  sectest_instance = NoradCli::SecTestContainer.new(ARGV[2], @@sectest_manifest['registry'],
                                                    @@sectest_manifest['version'], options)

  # Start the test
  sectest_instance.start

  # Print any debugging
  sectest_instance.output(options[:target]) if options[:debug]

  # Get the results
  results = sectest_instance.results

  say('Results are:', :green)
  formatted_results = options[:format] ? JSON.pretty_generate(JSON.parse(results)) : results
  puts formatted_results

  # Cleanup the sectest container
  sectest_instance.shutdown

  # Warn users if debugging is enabled
  say('Warning: Debug enabled: containers must be removed manually', :yellow) if options['debug']
end
extract_base_img(img_dir) click to toggle source

Check for a base image

# File lib/norad_cli/cli/sectest.rb, line 314
def extract_base_img(img_dir)
  from_line = File.readlines("#{img_dir}/Dockerfile").select { |line| line =~ /^FROM/ }
  # Detect missing FROM lines
  if from_line.length.zero?
    puts Rainbow("Error: #{img_dir}/Dockerfile missing a FROM line!").red
    exit(1)
  else
    from_line_arr = from_line[0].split(' ')
  end
  from_image = from_line[0][%r{\AFROM\s+(.*?\/)?(.*?)(:.*?)?\Z}i, 2] || raise('bad from')
  [from_image, from_line_arr[1]]
end
root_dir?() click to toggle source

Ensure commands are run from the root dir

# File lib/norad_cli/cli/sectest.rb, line 328
def root_dir?
  %w[base spec sectests].each do |dirrepo_name|
    if !File.exist?(dirrepo_name)
      say("Commands must be run from the root of the test repository\nExiting....", :red)
      exit(1)
    end
  end
end
run_specs(*specs, **opts) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 339
def run_specs(*specs, **opts)
  thor_options = opts.fetch(:thor_options, {})
  # Set environment variables
  if thor_options[:verbose]
    ENV['ENABLE_LOGS'] = 'true'
  end

  if thor_options[:debug]
    ENV['ENABLE_NORAD_DEBUG'] = 'true'
  end

  ENV['SCAN_ASSESSMENT'] = 'true'
  ENV['TEST_RESULTS_SERVER_IMAGE'] = 'docker-images-test-results-server'
  ENV['UBUNTU_SSH_SERVER_IMAGE'] = 'docker-images-test-ubuntu-ssh-server'

  codes = specs.map do |spec|
    # Run the tests
    if File.exist?("spec/#{spec}/#{spec}_spec.rb")
      RSpec.clear_examples
      say("Testing spec/#{spec}/#{spec}_spec.rb")
      RSpec::Core::Runner.run(["spec/#{spec}/#{spec}_spec.rb"], $stderr, $stdout)
    else
      say("Warning: spec/#{spec}/#{spec}_spec.rb does not exist!\n", :yellow)
      say("Warning: No tests will be run for #{spec}\n", :yellow)
      0 # always return 0 if the spec doesn't exist
    end
  end
  exit(codes.detect(-> { 0 }, &:nonzero?))
end
run_validations(*files) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 369
def run_validations(*files)
  codes = files.map do |file|
    ENV['sectest_name'] = file
    # Validate the readme file
    say("Validating README for #{file}")
    RSpec.clear_examples
    RSpec::Core::Runner.run(["#{File.dirname(File.expand_path(__FILE__))}/../support/readme_spec.rb"], $stderr, $stdout)

    # Validate the manifest file
    say("Validating manifest.yml for #{file}")
    RSpec.clear_examples
    RSpec::Core::Runner.run(["#{File.dirname(File.expand_path(__FILE__))}/../support/manifest_spec.rb"], $stderr, $stdout)
  end
  exit(codes.detect(-> { 0 }, &:nonzero?))
end
scaffold(sectest_name) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 80
def scaffold(sectest_name)
  # Grab the current directory
  repo_dir = Dir.pwd

  # Check for valid test types
  if !%w[authenticated web_application brute_force ssl_crypto ssh_crypto whole_host].include?(options[:test_type])
    say("#{options[:test_type]} is not a supported test type", :red)
    say('Exiting...', :red)
    exit(1)
  end

  # Set options for templates
  options[:name] = sectest_name
  options[:spec_class_name] = sectest_name.split('-').map { |t| t =~ /\d+/ ? t : t.capitalize! }.join

  # Error check to ensure this is a norad security test repository

  # Create the security tests standard files
  template('tool/Dockerfile.erb', "#{repo_dir}/sectests/#{sectest_name}/Dockerfile")
  template('tool/README.md.erb', "#{repo_dir}/sectests/#{sectest_name}/README.md")
  template('tool/manifest.yml.erb', "#{repo_dir}/sectests/#{sectest_name}/manifest.yml")

  # Create a starter wrapper script
  template('tool/wrapper.rb.erb', "#{repo_dir}/sectests/#{sectest_name}/#{sectest_name}-wrapper.rb")

  # Create the spec files
  template('tool/tool_spec.rb.erb', "#{repo_dir}/spec/#{sectest_name}/#{sectest_name}_spec.rb")
  if options[:test_type] == 'authenticated'
    template('tool/Dockerfile.auth.target.erb', "#{repo_dir}/spec/#{sectest_name}/targets/Dockerfile.secure")
    template('tool/Dockerfile.auth.target.erb', "#{repo_dir}/spec/#{sectest_name}/targets/Dockerfile.vulnerable")
  else
    template('tool/Dockerfile.unauth.target.erb', "#{repo_dir}/spec/#{sectest_name}/targets/Dockerfile.secure")
    template('tool/Dockerfile.unauth.target.erb', "#{repo_dir}/spec/#{sectest_name}/targets/Dockerfile.vulnerable")
  end
end
seed() click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 279
def seed
  # Generate the seed file
  SeedGenerator.process_manifests(options[:seedfile], options[:docsite])
end
spec() click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 266
def spec
  # Error check to ensure this is a plugin directory
  specs = Dir.glob('sectests/*').map do |f|
    if File.directory? f
      f.split('/')[-1]
    end
  end.compact
  run_specs(*specs, thor_options: options)
end
validate() click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 297
def validate
  # Error check to ensure this is a plugin directory
  files = Dir.glob('sectests/*').map do |f|
    if File.directory? f
      f.split('/')[-1]
    end
  end.compact
  run_validations(*files)
end

Private Instance Methods

parse_json(stream) click to toggle source
# File lib/norad_cli/cli/sectest.rb, line 388
def parse_json(stream)
  stream.split(/\r\n/).each do |string|
    parsed = JSON.parse(string)
    $stdout.print(parsed['stream']) if parsed.is_a?(Hash)
  end
rescue JSON::ParserError
  $stdout.print stream
end