#!/usr/bin/env ruby
# encoding: utf-8
# Copyright 2015 Dominik Richter. All rights reserved.
# author: Dominik Richter
# author: Christoph Hartmann

require 'thor'
require 'json'
require_relative '../lib/inspec'

class InspecCLI < Thor
  def self.target_options
    option :target, aliases: :t, type: :string, default: nil,
      desc: 'Simple targeting option using URIs, e.g. ssh://user:pass@host:port'
    option :backend, aliases: :b, type: :string, default: nil,
      desc: 'Choose a backend: local, ssh, winrm, docker.'
    option :host, type: :string,
      desc: 'Specify a remote host which is tested.'
    option :port, type: :numeric,
      desc: 'Specify the login port for a remote scan.'
    option :user, type: :string, default: nil,
      desc: 'The login user for a remote scan.'
    option :password, type: :string, default: nil,
      desc: 'Login password for a remote scan, if required.'
    option :key_files, type: :array, default: nil,
      desc: 'Login key or certificate file for a remote scan.'
    option :path, type: :string, default: nil,
      desc: 'Login path to use when connecting to the target (WinRM).'
    option :sudo, type: :boolean, default: false,
      desc: 'Run scans with sudo. Only activates on Unix and non-root user.'
    option :sudo_password, type: :string, default: nil,
      desc: 'Specify a sudo password, if it is required.'
    option :sudo_options, type: :string, default: '',
      desc: 'Additional sudo options for a remote scan.'
    option :ssl, type: :boolean, default: false,
      desc: 'Use SSL for transport layer encryption (WinRM).'
    option :self_signed, type: :boolean, default: false,
      desc: 'Allow remote scans with self-signed certificates (WinRM).'
  end

  desc 'json PATH', 'read all tests in PATH and generate a JSON profile'
  option :id, type: :string,
    desc: 'Attach a profile ID to all test results'
  option :output, aliases: :o, type: :string,
    desc: 'Save the created profile to a path'
  def json(path)
    profile = Inspec::Profile.from_path(path, options)
    dst = options[:output].to_s
    if dst.empty?
      puts JSON.pretty_generate(profile.info)
    else
      if File.exist? dst
        puts "----> updating #{dst}"
      else
        puts "----> creating #{dst}"
      end
      fdst = File.expand_path(dst)
      File.write(fdst, JSON.dump(profile.info))
    end
  end

  desc 'check PATH', 'verify test structure in PATH'
  def check(path)
    o = options.dup
    o[:logger] = Logger.new(STDOUT)
    profile = Inspec::Profile.from_path(path, o)
    exit 1 unless profile.check
  end

  desc 'exec PATHS', 'run all test files'
  option :id, type: :string,
    desc: 'Attach a profile ID to all test results'
  target_options
  option :format, type: :string, default: 'progress'
  def exec(*tests)
    runner = Inspec::Runner.new(options)
    runner.add_tests(tests)
    runner.run
  rescue RuntimeError => e
    puts e.message
  end

  desc 'detect', 'detect the target OS'
  target_options
  def detect
    runner = Inspec::Runner.new(options)
    rel = File.join(File.dirname(__FILE__), *%w{.. lib utils detect.rb})
    detect_util = File.expand_path(rel)
    runner.add_tests([detect_util])
    runner.run
  rescue RuntimeError => e
    puts e.message
  end

  desc 'shell', 'open an interactive debugging shell'
  target_options
  def shell_func
    runner = Inspec::Runner.new(options)
    Inspec::Shell.new(runner).start
  rescue RuntimeError => e
    puts e.message
  end

  desc 'version', 'prints the version of this tool'
  def version
    puts Inspec::VERSION
  end
end
InspecCLI.start(ARGV)
