inspec_tools 0.0.0.1.ENOTAG
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/CHANGELOG.md +662 -0
 - data/LICENSE.md +15 -0
 - data/README.md +329 -0
 - data/Rakefile +30 -0
 - data/exe/inspec_tools +14 -0
 - data/lib/data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx +0 -0
 - data/lib/data/NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx +0 -0
 - data/lib/data/README.TXT +25 -0
 - data/lib/data/U_CCI_List.xml +38403 -0
 - data/lib/data/attributes.yml +23 -0
 - data/lib/data/cci2html.xsl +136 -0
 - data/lib/data/mapping.yml +17 -0
 - data/lib/data/stig.csv +1 -0
 - data/lib/data/threshold.yaml +83 -0
 - data/lib/exceptions/impact_input_error.rb +6 -0
 - data/lib/exceptions/severity_input_error.rb +6 -0
 - data/lib/happy_mapper_tools/benchmark.rb +161 -0
 - data/lib/happy_mapper_tools/cci_attributes.rb +66 -0
 - data/lib/happy_mapper_tools/stig_attributes.rb +196 -0
 - data/lib/happy_mapper_tools/stig_checklist.rb +99 -0
 - data/lib/inspec_tools.rb +17 -0
 - data/lib/inspec_tools/ckl.rb +20 -0
 - data/lib/inspec_tools/cli.rb +31 -0
 - data/lib/inspec_tools/csv.rb +101 -0
 - data/lib/inspec_tools/help.rb +9 -0
 - data/lib/inspec_tools/help/compliance.md +7 -0
 - data/lib/inspec_tools/help/csv2inspec.md +5 -0
 - data/lib/inspec_tools/help/inspec2ckl.md +5 -0
 - data/lib/inspec_tools/help/inspec2csv.md +5 -0
 - data/lib/inspec_tools/help/inspec2xccdf.md +5 -0
 - data/lib/inspec_tools/help/pdf2inspec.md +6 -0
 - data/lib/inspec_tools/help/summary.md +5 -0
 - data/lib/inspec_tools/help/xccdf2inspec.md +5 -0
 - data/lib/inspec_tools/inspec.rb +331 -0
 - data/lib/inspec_tools/pdf.rb +136 -0
 - data/lib/inspec_tools/plugin.rb +15 -0
 - data/lib/inspec_tools/plugin_cli.rb +278 -0
 - data/lib/inspec_tools/summary.rb +126 -0
 - data/lib/inspec_tools/version.rb +8 -0
 - data/lib/inspec_tools/xccdf.rb +155 -0
 - data/lib/inspec_tools/xlsx_tool.rb +148 -0
 - data/lib/inspec_tools_plugin.rb +7 -0
 - data/lib/overrides/false_class.rb +5 -0
 - data/lib/overrides/nil_class.rb +5 -0
 - data/lib/overrides/object.rb +5 -0
 - data/lib/overrides/string.rb +5 -0
 - data/lib/overrides/true_class.rb +5 -0
 - data/lib/utilities/csv_util.rb +14 -0
 - data/lib/utilities/extract_nist_cis_mapping.rb +57 -0
 - data/lib/utilities/extract_pdf_text.rb +20 -0
 - data/lib/utilities/inspec_util.rb +435 -0
 - data/lib/utilities/parser.rb +373 -0
 - data/lib/utilities/text_cleaner.rb +69 -0
 - metadata +359 -0
 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module InspecToolsPlugin
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Plugin < Inspec.plugin(2)
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Metadata
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Must match entry in plugins.json
         
     | 
| 
      
 7 
     | 
    
         
            +
                plugin_name :'inspec-tools_plugin'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                # Activation hooks (CliCommand as an example)
         
     | 
| 
      
 10 
     | 
    
         
            +
                cli_command :tools do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  require_relative 'plugin_cli'
         
     | 
| 
      
 12 
     | 
    
         
            +
                  InspecPlugins::InspecToolsPlugin::CliCommand
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,278 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'roo'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative '../utilities/inspec_util'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative '../utilities/csv_util'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module InspecTools
         
     | 
| 
      
 8 
     | 
    
         
            +
              autoload :Help, 'inspec_tools/help'
         
     | 
| 
      
 9 
     | 
    
         
            +
              autoload :Command, 'inspec_tools/command'
         
     | 
| 
      
 10 
     | 
    
         
            +
              autoload :XCCDF, 'inspec_tools/xccdf'
         
     | 
| 
      
 11 
     | 
    
         
            +
              autoload :PDF, 'inspec_tools/pdf'
         
     | 
| 
      
 12 
     | 
    
         
            +
              autoload :CSVTool, 'inspec_tools/csv'
         
     | 
| 
      
 13 
     | 
    
         
            +
              autoload :CKL, 'inspec_tools/ckl'
         
     | 
| 
      
 14 
     | 
    
         
            +
              autoload :Inspec, 'inspec_tools/inspec'
         
     | 
| 
      
 15 
     | 
    
         
            +
              autoload :Summary, 'inspec_tools/summary'
         
     | 
| 
      
 16 
     | 
    
         
            +
              autoload :Threshold, 'inspec_tools/threshold'
         
     | 
| 
      
 17 
     | 
    
         
            +
              autoload :XLSXTool, 'inspec_tools/xlsx_tool'
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            # rubocop:disable Style/GuardClause
         
     | 
| 
      
 21 
     | 
    
         
            +
            module InspecPlugins
         
     | 
| 
      
 22 
     | 
    
         
            +
              module InspecToolsPlugin
         
     | 
| 
      
 23 
     | 
    
         
            +
                class CliCommand < Inspec.plugin(2, :cli_command) # rubocop:disable Metrics/ClassLength
         
     | 
| 
      
 24 
     | 
    
         
            +
                  POSSIBLE_LOG_LEVELS = %w{debug info warn error fatal}.freeze
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  class_option :log_directory, type: :string, aliases: :l, desc: 'Provie log location'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  class_option :log_level, type: :string, desc: "Set the logging level: #{POSSIBLE_LOG_LEVELS}"
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  subcommand_desc 'tools [COMMAND]', 'Runs inspec_tools commands through Inspec'
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  desc 'xccdf2inspec', 'xccdf2inspec translates an xccdf file to an inspec profile'
         
     | 
| 
      
 32 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:xccdf2inspec)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  option :xccdf, required: true, aliases: '-x'
         
     | 
| 
      
 34 
     | 
    
         
            +
                  option :attributes, required: false, aliases: '-a'
         
     | 
| 
      
 35 
     | 
    
         
            +
                  option :output, required: false, aliases: '-o', default: 'profile'
         
     | 
| 
      
 36 
     | 
    
         
            +
                  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
         
     | 
| 
      
 37 
     | 
    
         
            +
                  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
         
     | 
| 
      
 38 
     | 
    
         
            +
                  option :replace_tags, type: :array, required: false, aliases: '-r'
         
     | 
| 
      
 39 
     | 
    
         
            +
                  option :metadata, required: false, aliases: '-m'
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def xccdf2inspec
         
     | 
| 
      
 41 
     | 
    
         
            +
                    xccdf = InspecTools::XCCDF.new(File.read(options[:xccdf]), options[:replace_tags])
         
     | 
| 
      
 42 
     | 
    
         
            +
                    profile = xccdf.to_inspec
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    if !options[:metadata].nil?
         
     | 
| 
      
 45 
     | 
    
         
            +
                      xccdf.inject_metadata(File.read(options[:metadata]))
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
         
     | 
| 
      
 49 
     | 
    
         
            +
                    if !options[:attributes].nil?
         
     | 
| 
      
 50 
     | 
    
         
            +
                      attributes = xccdf.to_attributes
         
     | 
| 
      
 51 
     | 
    
         
            +
                      File.write(options[:attributes], YAML.dump(attributes))
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  desc 'inspec2xccdf', 'inspec2xccdf translates an inspec profile and attributes files to an xccdf file'
         
     | 
| 
      
 56 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:inspec2xccdf)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  option :inspec_json, required: true, aliases: '-j'
         
     | 
| 
      
 58 
     | 
    
         
            +
                  option :attributes,  required: true, aliases: '-a'
         
     | 
| 
      
 59 
     | 
    
         
            +
                  option :output, required: true, aliases: '-o'
         
     | 
| 
      
 60 
     | 
    
         
            +
                  def inspec2xccdf
         
     | 
| 
      
 61 
     | 
    
         
            +
                    json = File.read(options[:inspec_json])
         
     | 
| 
      
 62 
     | 
    
         
            +
                    inspec_tool = InspecTools::Inspec.new(json)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    attr_hsh = YAML.load_file(options[:attributes])
         
     | 
| 
      
 64 
     | 
    
         
            +
                    xccdf = inspec_tool.to_xccdf(attr_hsh)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    File.write(options[:output], xccdf)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  desc 'csv2inspec', 'csv2inspec translates CSV to Inspec controls using a mapping file'
         
     | 
| 
      
 69 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:csv2inspec)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  option :csv, required: true, aliases: '-c'
         
     | 
| 
      
 71 
     | 
    
         
            +
                  option :mapping, required: true, aliases: '-m'
         
     | 
| 
      
 72 
     | 
    
         
            +
                  option :verbose, required: false, type: :boolean, aliases: '-V'
         
     | 
| 
      
 73 
     | 
    
         
            +
                  option :output, required: false, aliases: '-o', default: 'profile'
         
     | 
| 
      
 74 
     | 
    
         
            +
                  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
         
     | 
| 
      
 75 
     | 
    
         
            +
                  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
         
     | 
| 
      
 76 
     | 
    
         
            +
                  def csv2inspec
         
     | 
| 
      
 77 
     | 
    
         
            +
                    csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
         
     | 
| 
      
 78 
     | 
    
         
            +
                    mapping = YAML.load_file(options[:mapping])
         
     | 
| 
      
 79 
     | 
    
         
            +
                    profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
         
     | 
| 
      
 80 
     | 
    
         
            +
                    Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  desc 'xlsx2inspec', 'xlsx2inspec translates CIS XLSX to Inspec controls using a mapping file'
         
     | 
| 
      
 84 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:xlsx2inspec)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  option :xlsx, required: true, aliases: '-x'
         
     | 
| 
      
 86 
     | 
    
         
            +
                  option :mapping, required: true, aliases: '-m'
         
     | 
| 
      
 87 
     | 
    
         
            +
                  option :control_name_prefix, required: true, aliases: '-p'
         
     | 
| 
      
 88 
     | 
    
         
            +
                  option :verbose, required: false, type: :boolean, aliases: '-V'
         
     | 
| 
      
 89 
     | 
    
         
            +
                  option :output, required: false, aliases: '-o', default: 'profile'
         
     | 
| 
      
 90 
     | 
    
         
            +
                  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
         
     | 
| 
      
 91 
     | 
    
         
            +
                  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def xlsx2inspec
         
     | 
| 
      
 93 
     | 
    
         
            +
                    xlsx = Roo::Spreadsheet.open(options[:xlsx])
         
     | 
| 
      
 94 
     | 
    
         
            +
                    mapping = YAML.load_file(options[:mapping])
         
     | 
| 
      
 95 
     | 
    
         
            +
                    profile = InspecTools::XLSXTool.new(xlsx, mapping, options[:xlsx].split('/')[-1].split('.')[0], options[:verbose]).to_inspec(options[:control_name_prefix])
         
     | 
| 
      
 96 
     | 
    
         
            +
                    Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  desc 'inspec2csv', 'inspec2csv translates Inspec controls to CSV'
         
     | 
| 
      
 100 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:inspec2csv)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  option :inspec_json, required: true, aliases: '-j'
         
     | 
| 
      
 102 
     | 
    
         
            +
                  option :output, required: true, aliases: '-o'
         
     | 
| 
      
 103 
     | 
    
         
            +
                  option :verbose, required: false, type: :boolean, aliases: '-V'
         
     | 
| 
      
 104 
     | 
    
         
            +
                  def inspec2csv
         
     | 
| 
      
 105 
     | 
    
         
            +
                    csv = InspecTools::Inspec.new(File.read(options[:inspec_json])).to_csv
         
     | 
| 
      
 106 
     | 
    
         
            +
                    Utils::CSVUtil.unpack_csv(csv, options[:output])
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  desc 'inspec2ckl', 'inspec2ckl translates an inspec json file to a Checklist file'
         
     | 
| 
      
 110 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:inspec2ckl)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  option :inspec_json, required: true, aliases: '-j'
         
     | 
| 
      
 112 
     | 
    
         
            +
                  option :output, required: true, aliases: '-o'
         
     | 
| 
      
 113 
     | 
    
         
            +
                  option :verbose, type: :boolean, aliases: '-V'
         
     | 
| 
      
 114 
     | 
    
         
            +
                  option :metadata, required: false, aliases: '-m'
         
     | 
| 
      
 115 
     | 
    
         
            +
                  def inspec2ckl
         
     | 
| 
      
 116 
     | 
    
         
            +
                    metadata = '{}'
         
     | 
| 
      
 117 
     | 
    
         
            +
                    if !options[:metadata].nil?
         
     | 
| 
      
 118 
     | 
    
         
            +
                      metadata = File.read(options[:metadata])
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
                    ckl = InspecTools::Inspec.new(File.read(options[:inspec_json]), metadata).to_ckl
         
     | 
| 
      
 121 
     | 
    
         
            +
                    File.write(options[:output], ckl)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                  desc 'pdf2inspec', 'pdf2inspec translates a PDF Security Control Speficication to Inspec Security Profile'
         
     | 
| 
      
 125 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:pdf2inspec)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  option :pdf, required: true, aliases: '-p'
         
     | 
| 
      
 127 
     | 
    
         
            +
                  option :output, required: false, aliases: '-o', default: 'profile'
         
     | 
| 
      
 128 
     | 
    
         
            +
                  option :debug, required: false, aliases: '-d', type: :boolean, default: false
         
     | 
| 
      
 129 
     | 
    
         
            +
                  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
         
     | 
| 
      
 130 
     | 
    
         
            +
                  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def pdf2inspec
         
     | 
| 
      
 132 
     | 
    
         
            +
                    pdf = File.open(options[:pdf])
         
     | 
| 
      
 133 
     | 
    
         
            +
                    profile = InspecTools::PDF.new(pdf, options[:output], options[:debug]).to_inspec
         
     | 
| 
      
 134 
     | 
    
         
            +
                    Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  desc 'generate_map', 'Generates mapping template from CSV to Inspec Controls'
         
     | 
| 
      
 138 
     | 
    
         
            +
                  def generate_map
         
     | 
| 
      
 139 
     | 
    
         
            +
                    template = '
         
     | 
| 
      
 140 
     | 
    
         
            +
                    # Setting csv_header to true will skip the csv file header
         
     | 
| 
      
 141 
     | 
    
         
            +
                    skip_csv_header: true
         
     | 
| 
      
 142 
     | 
    
         
            +
                    width   : 80
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    control.id: 0
         
     | 
| 
      
 146 
     | 
    
         
            +
                    control.title: 15
         
     | 
| 
      
 147 
     | 
    
         
            +
                    control.desc: 16
         
     | 
| 
      
 148 
     | 
    
         
            +
                    control.tags:
         
     | 
| 
      
 149 
     | 
    
         
            +
                            severity: 1
         
     | 
| 
      
 150 
     | 
    
         
            +
                            rid: 8
         
     | 
| 
      
 151 
     | 
    
         
            +
                            stig_id: 3
         
     | 
| 
      
 152 
     | 
    
         
            +
                            cci: 2
         
     | 
| 
      
 153 
     | 
    
         
            +
                            check: 12
         
     | 
| 
      
 154 
     | 
    
         
            +
                            fix: 10
         
     | 
| 
      
 155 
     | 
    
         
            +
                    '
         
     | 
| 
      
 156 
     | 
    
         
            +
                    myfile = File.new('mapping.yml', 'w')
         
     | 
| 
      
 157 
     | 
    
         
            +
                    myfile.puts template
         
     | 
| 
      
 158 
     | 
    
         
            +
                    myfile.close
         
     | 
| 
      
 159 
     | 
    
         
            +
                  end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                  desc 'generate_ckl_metadata', 'Generate metadata file that can be passed to inspec2ckl'
         
     | 
| 
      
 162 
     | 
    
         
            +
                  def generate_ckl_metadata
         
     | 
| 
      
 163 
     | 
    
         
            +
                    metadata = {}
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                    metadata['stigid'] = ask('STID ID: ')
         
     | 
| 
      
 166 
     | 
    
         
            +
                    metadata['role'] = ask('Role: ')
         
     | 
| 
      
 167 
     | 
    
         
            +
                    metadata['type'] = ask('Type: ')
         
     | 
| 
      
 168 
     | 
    
         
            +
                    metadata['hostname'] = ask('Hostname: ')
         
     | 
| 
      
 169 
     | 
    
         
            +
                    metadata['ip'] = ask('IP Address: ')
         
     | 
| 
      
 170 
     | 
    
         
            +
                    metadata['mac'] = ask('MAC Address: ')
         
     | 
| 
      
 171 
     | 
    
         
            +
                    metadata['fqdn'] = ask('FQDN: ')
         
     | 
| 
      
 172 
     | 
    
         
            +
                    metadata['tech_area'] = ask('Tech Area: ')
         
     | 
| 
      
 173 
     | 
    
         
            +
                    metadata['target_key'] = ask('Target Key: ')
         
     | 
| 
      
 174 
     | 
    
         
            +
                    metadata['web_or_database'] = ask('Web or Database: ')
         
     | 
| 
      
 175 
     | 
    
         
            +
                    metadata['web_db_site'] = ask('Web DB Site: ')
         
     | 
| 
      
 176 
     | 
    
         
            +
                    metadata['web_db_instance'] = ask('Web DB Instance: ')
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                    metadata.delete_if { |_key, value| value.empty? }
         
     | 
| 
      
 179 
     | 
    
         
            +
                    File.open('metadata.json', 'w') do |f|
         
     | 
| 
      
 180 
     | 
    
         
            +
                      f.write(metadata.to_json)
         
     | 
| 
      
 181 
     | 
    
         
            +
                    end
         
     | 
| 
      
 182 
     | 
    
         
            +
                  end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                  desc 'generate_inspec_metadata', 'Generate mapping file that can be passed to xccdf2inspec'
         
     | 
| 
      
 185 
     | 
    
         
            +
                  def generate_inspec_metadata
         
     | 
| 
      
 186 
     | 
    
         
            +
                    metadata = {}
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                    metadata['maintainer'] = ask('Maintainer: ')
         
     | 
| 
      
 189 
     | 
    
         
            +
                    metadata['copyright'] = ask('Copyright: ')
         
     | 
| 
      
 190 
     | 
    
         
            +
                    metadata['copyright_email'] = ask('Copyright Email: ')
         
     | 
| 
      
 191 
     | 
    
         
            +
                    metadata['license'] = ask('License: ')
         
     | 
| 
      
 192 
     | 
    
         
            +
                    metadata['version'] = ask('Version: ')
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                    metadata.delete_if { |_key, value| value.empty? }
         
     | 
| 
      
 195 
     | 
    
         
            +
                    File.open('metadata.json', 'w') do |f|
         
     | 
| 
      
 196 
     | 
    
         
            +
                      f.write(metadata.to_json)
         
     | 
| 
      
 197 
     | 
    
         
            +
                    end
         
     | 
| 
      
 198 
     | 
    
         
            +
                  end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                  desc 'summary', 'summary parses an inspec results json to create a summary json'
         
     | 
| 
      
 201 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:summary)
         
     | 
| 
      
 202 
     | 
    
         
            +
                  option :inspec_json, required: true, aliases: '-j'
         
     | 
| 
      
 203 
     | 
    
         
            +
                  option :output, required: false, aliases: '-o'
         
     | 
| 
      
 204 
     | 
    
         
            +
                  option :cli, type: :boolean, required: false, aliases: '-c'
         
     | 
| 
      
 205 
     | 
    
         
            +
                  option :verbose, type: :boolean, aliases: '-V'
         
     | 
| 
      
 206 
     | 
    
         
            +
                  option :json_full, type: :boolean, required: false, aliases: '-f'
         
     | 
| 
      
 207 
     | 
    
         
            +
                  option :json_counts, type: :boolean, required: false, aliases: '-k'
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                  def summary
         
     | 
| 
      
 210 
     | 
    
         
            +
                    summary = InspecTools::Summary.new(File.read(options[:inspec_json])).to_summary
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                    if options[:cli]
         
     | 
| 
      
 213 
     | 
    
         
            +
                      puts "\nOverall compliance: #{summary[:compliance]}%\n\n"
         
     | 
| 
      
 214 
     | 
    
         
            +
                      summary[:status].keys.each do |category|
         
     | 
| 
      
 215 
     | 
    
         
            +
                        puts category
         
     | 
| 
      
 216 
     | 
    
         
            +
                        summary[:status][category].keys.each do |impact|
         
     | 
| 
      
 217 
     | 
    
         
            +
                          puts "\t#{impact} : #{summary[:status][category][impact]}"
         
     | 
| 
      
 218 
     | 
    
         
            +
                        end
         
     | 
| 
      
 219 
     | 
    
         
            +
                      end
         
     | 
| 
      
 220 
     | 
    
         
            +
                    end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                    json_summary = summary.to_json
         
     | 
| 
      
 223 
     | 
    
         
            +
                    File.write(options[:output], json_summary) if options[:output]
         
     | 
| 
      
 224 
     | 
    
         
            +
                    puts json_summary if options[:json_full]
         
     | 
| 
      
 225 
     | 
    
         
            +
                    puts summary[:status].to_json if options[:json_counts]
         
     | 
| 
      
 226 
     | 
    
         
            +
                  end
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                  desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
         
     | 
| 
      
 229 
     | 
    
         
            +
                  long_desc InspecTools::Help.text(:compliance)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  option :inspec_json, required: true, aliases: '-j'
         
     | 
| 
      
 231 
     | 
    
         
            +
                  option :threshold_file, required: false, aliases: '-f'
         
     | 
| 
      
 232 
     | 
    
         
            +
                  option :threshold_inline, required: false, aliases: '-i'
         
     | 
| 
      
 233 
     | 
    
         
            +
                  option :verbose, type: :boolean, aliases: '-V'
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                  def compliance
         
     | 
| 
      
 236 
     | 
    
         
            +
                    if options[:threshold_file].nil? && options[:threshold_inline].nil?
         
     | 
| 
      
 237 
     | 
    
         
            +
                      puts 'Please provide threshold as a yaml file or inline yaml'
         
     | 
| 
      
 238 
     | 
    
         
            +
                      exit(1)
         
     | 
| 
      
 239 
     | 
    
         
            +
                    end
         
     | 
| 
      
 240 
     | 
    
         
            +
                    threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
         
     | 
| 
      
 241 
     | 
    
         
            +
                    threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
         
     | 
| 
      
 242 
     | 
    
         
            +
                    compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    compliance ? exit(0) : exit(1)
         
     | 
| 
      
 244 
     | 
    
         
            +
                  end
         
     | 
| 
      
 245 
     | 
    
         
            +
                end
         
     | 
| 
      
 246 
     | 
    
         
            +
              end
         
     | 
| 
      
 247 
     | 
    
         
            +
            end
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
            #=====================================================================#
         
     | 
| 
      
 250 
     | 
    
         
            +
            #                        Pre-Flight Code
         
     | 
| 
      
 251 
     | 
    
         
            +
            #=====================================================================#
         
     | 
| 
      
 252 
     | 
    
         
            +
            help_commands = ['-h', '--help', 'help']
         
     | 
| 
      
 253 
     | 
    
         
            +
            log_commands = ['-l', '--log-directory']
         
     | 
| 
      
 254 
     | 
    
         
            +
            version_commands = ['-v', '--version', 'version']
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
            #---------------------------------------------------------------------#
         
     | 
| 
      
 257 
     | 
    
         
            +
            # Adjustments for non-required version commands
         
     | 
| 
      
 258 
     | 
    
         
            +
            #---------------------------------------------------------------------#
         
     | 
| 
      
 259 
     | 
    
         
            +
            unless (version_commands & ARGV).empty?
         
     | 
| 
      
 260 
     | 
    
         
            +
              puts InspecTools::VERSION
         
     | 
| 
      
 261 
     | 
    
         
            +
              exit 0
         
     | 
| 
      
 262 
     | 
    
         
            +
            end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
            #---------------------------------------------------------------------#
         
     | 
| 
      
 265 
     | 
    
         
            +
            # Adjustments for non-required log-directory
         
     | 
| 
      
 266 
     | 
    
         
            +
            #---------------------------------------------------------------------#
         
     | 
| 
      
 267 
     | 
    
         
            +
            ARGV.push("--log-directory=#{Dir.pwd}/logs") if (log_commands & ARGV).empty? && (help_commands & ARGV).empty?
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
            # Push help to front of command so thor recognizes subcommands are called with help
         
     | 
| 
      
 270 
     | 
    
         
            +
            if help_commands.any? { |cmd| ARGV.include? cmd }
         
     | 
| 
      
 271 
     | 
    
         
            +
              help_commands.each do |cmd|
         
     | 
| 
      
 272 
     | 
    
         
            +
                if (match = ARGV.delete(cmd))
         
     | 
| 
      
 273 
     | 
    
         
            +
                  ARGV.unshift match
         
     | 
| 
      
 274 
     | 
    
         
            +
                end
         
     | 
| 
      
 275 
     | 
    
         
            +
              end
         
     | 
| 
      
 276 
     | 
    
         
            +
            end
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
      
 278 
     | 
    
         
            +
            # rubocop:enable Style/GuardClause
         
     | 
| 
         @@ -0,0 +1,126 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative '../utilities/inspec_util'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # Impact Definitions
         
     | 
| 
      
 8 
     | 
    
         
            +
            CRITICAL = 0.9
         
     | 
| 
      
 9 
     | 
    
         
            +
            HIGH = 0.7
         
     | 
| 
      
 10 
     | 
    
         
            +
            MEDIUM = 0.5
         
     | 
| 
      
 11 
     | 
    
         
            +
            LOW = 0.3
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            BUCKETS = %w{failed passed no_impact skipped error}.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
            TALLYS = %w{total critical high medium low}.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            THRESHOLD_TEMPLATE = File.expand_path('../data/threshold.yaml', File.dirname(__FILE__))
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            module InspecTools
         
     | 
| 
      
 19 
     | 
    
         
            +
              class Summary
         
     | 
| 
      
 20 
     | 
    
         
            +
                def initialize(inspec_json)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @json = JSON.parse(inspec_json)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def to_summary
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @data = Utils::InspecUtil.parse_data_for_ckl(@json)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @summary = {}
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @data.keys.each do |control_id|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    current_control = @data[control_id]
         
     | 
| 
      
 29 
     | 
    
         
            +
                    current_control[:compliance_status] = Utils::InspecUtil.control_status(current_control, true)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    current_control[:finding_details] = Utils::InspecUtil.control_finding_details(current_control, current_control[:compliance_status])
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  compute_summary
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @summary
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def threshold(threshold = nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @summary = to_summary
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @threshold = Utils::InspecUtil.to_dotted_hash(YAML.load_file(THRESHOLD_TEMPLATE))
         
     | 
| 
      
 39 
     | 
    
         
            +
                  parse_threshold(Utils::InspecUtil.to_dotted_hash(threshold))
         
     | 
| 
      
 40 
     | 
    
         
            +
                  threshold_compliance
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                private
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def compute_summary
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @summary[:buckets] = {}
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @summary[:buckets][:failed]    = select_by_status(@data, 'Open')
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @summary[:buckets][:passed]    = select_by_status(@data, 'NotAFinding')
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @summary[:buckets][:no_impact] = select_by_status(@data, 'Not_Applicable')
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @summary[:buckets][:skipped]   = select_by_status(@data, 'Not_Reviewed')
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @summary[:buckets][:error]     = select_by_status(@data, 'Profile_Error')
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  @summary[:status] = {}
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @summary[:status][:failed]    = tally_by_impact(@summary[:buckets][:failed])
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @summary[:status][:passed]    = tally_by_impact(@summary[:buckets][:passed])
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @summary[:status][:no_impact] = tally_by_impact(@summary[:buckets][:no_impact])
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @summary[:status][:skipped]   = tally_by_impact(@summary[:buckets][:skipped])
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @summary[:status][:error]     = tally_by_impact(@summary[:buckets][:error])
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  @summary[:compliance] = compute_compliance
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def select_by_impact(controls, impact)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  controls.select { |_key, value| value[:impact].to_f.eql?(impact) }
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def select_by_status(controls, status)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  controls.select { |_key, value| value[:compliance_status].eql?(status) }
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def tally_by_impact(controls)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  tally = {}
         
     | 
| 
      
 73 
     | 
    
         
            +
                  tally[:total]    = controls.count
         
     | 
| 
      
 74 
     | 
    
         
            +
                  tally[:critical] = select_by_impact(controls, CRITICAL).count
         
     | 
| 
      
 75 
     | 
    
         
            +
                  tally[:high]     = select_by_impact(controls, HIGH).count
         
     | 
| 
      
 76 
     | 
    
         
            +
                  tally[:medium]   = select_by_impact(controls, MEDIUM).count
         
     | 
| 
      
 77 
     | 
    
         
            +
                  tally[:low]      = select_by_impact(controls, LOW).count
         
     | 
| 
      
 78 
     | 
    
         
            +
                  tally
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def compute_compliance
         
     | 
| 
      
 82 
     | 
    
         
            +
                  (@summary[:status][:passed][:total]*100.0/
         
     | 
| 
      
 83 
     | 
    
         
            +
                    (@summary[:status][:passed][:total]+
         
     | 
| 
      
 84 
     | 
    
         
            +
                     @summary[:status][:failed][:total]+
         
     | 
| 
      
 85 
     | 
    
         
            +
                     @summary[:status][:skipped][:total]+
         
     | 
| 
      
 86 
     | 
    
         
            +
                     @summary[:status][:error][:total])).round(1)
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def threshold_compliance
         
     | 
| 
      
 90 
     | 
    
         
            +
                  compliance = true
         
     | 
| 
      
 91 
     | 
    
         
            +
                  failure = []
         
     | 
| 
      
 92 
     | 
    
         
            +
                  max = @threshold['compliance.max']
         
     | 
| 
      
 93 
     | 
    
         
            +
                  min = @threshold['compliance.min']
         
     | 
| 
      
 94 
     | 
    
         
            +
                  if max != -1 and @summary[:compliance] > max
         
     | 
| 
      
 95 
     | 
    
         
            +
                    compliance = false
         
     | 
| 
      
 96 
     | 
    
         
            +
                    failure << "Expected compliance.max:#{max} got:#{@summary[:compliance]}"
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                  if min != -1 and @summary[:compliance] < min
         
     | 
| 
      
 99 
     | 
    
         
            +
                    compliance = false
         
     | 
| 
      
 100 
     | 
    
         
            +
                    failure << "Expected compliance.min:#{min} got:#{@summary[:compliance]}"
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                  status = @summary[:status]
         
     | 
| 
      
 103 
     | 
    
         
            +
                  BUCKETS.each do |bucket|
         
     | 
| 
      
 104 
     | 
    
         
            +
                    TALLYS.each do |tally|
         
     | 
| 
      
 105 
     | 
    
         
            +
                      max = @threshold["#{bucket}.#{tally}.max"]
         
     | 
| 
      
 106 
     | 
    
         
            +
                      min = @threshold["#{bucket}.#{tally}.min"]
         
     | 
| 
      
 107 
     | 
    
         
            +
                      if max != -1 and status[bucket.to_sym][tally.to_sym] > max
         
     | 
| 
      
 108 
     | 
    
         
            +
                        compliance = false
         
     | 
| 
      
 109 
     | 
    
         
            +
                        failure << "Expected #{bucket}.#{tally}.max:#{max} got:#{status[bucket.to_sym][tally.to_sym]}"
         
     | 
| 
      
 110 
     | 
    
         
            +
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
                      if min != -1 and status[bucket.to_sym][tally.to_sym] < min
         
     | 
| 
      
 112 
     | 
    
         
            +
                        compliance = false
         
     | 
| 
      
 113 
     | 
    
         
            +
                        failure << "Expected #{bucket}.#{tally}.min:#{min} got:#{status[bucket.to_sym][tally.to_sym]}"
         
     | 
| 
      
 114 
     | 
    
         
            +
                      end
         
     | 
| 
      
 115 
     | 
    
         
            +
                    end
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  puts failure.join("\n") unless compliance
         
     | 
| 
      
 118 
     | 
    
         
            +
                  puts 'Compliance threshold met' if compliance
         
     | 
| 
      
 119 
     | 
    
         
            +
                  compliance
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                def parse_threshold(new_threshold)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @threshold.merge!(new_threshold)
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
              end
         
     | 
| 
      
 126 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,155 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative '../happy_mapper_tools/stig_attributes'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative '../happy_mapper_tools/cci_attributes'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative '../utilities/inspec_util'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'digest'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module InspecTools
         
     | 
| 
      
 9 
     | 
    
         
            +
              # rubocop:disable Metrics/ClassLength
         
     | 
| 
      
 10 
     | 
    
         
            +
              # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 11 
     | 
    
         
            +
              # rubocop:disable Metrics/PerceivedComplexity
         
     | 
| 
      
 12 
     | 
    
         
            +
              # rubocop:disable Metrics/CyclomaticComplexity
         
     | 
| 
      
 13 
     | 
    
         
            +
              # rubocop:disable Metrics/BlockLength
         
     | 
| 
      
 14 
     | 
    
         
            +
              class XCCDF
         
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(xccdf, replace_tags = nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @xccdf = xccdf
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @xccdf = replace_tags_in_xccdf(replace_tags, @xccdf) unless replace_tags.nil?
         
     | 
| 
      
 18 
     | 
    
         
            +
                  cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read(cci_list_path))
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read('./data/U_CCI_List.xml'))
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @benchmark = HappyMapperTools::StigAttributes::Benchmark.parse(@xccdf)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def to_ckl
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # TODO: to_ckl
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def to_csv
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # TODO: to_csv
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def to_inspec
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @profile = {}
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @controls = []
         
     | 
| 
      
 35 
     | 
    
         
            +
                  insert_json_metadata
         
     | 
| 
      
 36 
     | 
    
         
            +
                  insert_controls
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @profile
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                ####
         
     | 
| 
      
 42 
     | 
    
         
            +
                # extracts non-InSpec attributes
         
     | 
| 
      
 43 
     | 
    
         
            +
                ###
         
     | 
| 
      
 44 
     | 
    
         
            +
                # TODO there may be more attributes we want to extract, see data/attributes.yml for example
         
     | 
| 
      
 45 
     | 
    
         
            +
                def to_attributes # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @attribute = {}
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  @attribute['benchmark.title'] = @benchmark.title
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @attribute['benchmark.id'] = @benchmark.id
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @attribute['benchmark.description'] = @benchmark.description
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @attribute['benchmark.version'] = @benchmark.version
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  @attribute['benchmark.status'] = @benchmark.status
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @attribute['benchmark.status.date'] = @benchmark.release_date.release_date
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  @attribute['benchmark.notice.id'] = @benchmark.notice.id
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  @attribute['benchmark.plaintext'] = @benchmark.plaintext.plaintext
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @attribute['benchmark.plaintext.id'] = @benchmark.plaintext.id
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  @attribute['reference.href'] = @benchmark.reference.href
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @attribute['reference.dc.publisher'] = @benchmark.reference.dc_publisher
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @attribute['reference.dc.source'] = @benchmark.reference.dc_source
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @attribute['reference.dc.title'] = @benchmark.group[0].rule.reference.dc_title if !@benchmark.group[0].nil?
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @attribute['reference.dc.subject'] = @benchmark.group[0].rule.reference.dc_subject if !@benchmark.group[0].nil?
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @attribute['reference.dc.type'] = @benchmark.group[0].rule.reference.dc_type if !@benchmark.group[0].nil?
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @attribute['reference.dc.identifier'] = @benchmark.group[0].rule.reference.dc_identifier if !@benchmark.group[0].nil?
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  @attribute['content_ref.name'] = @benchmark.group[0].rule.check.content_ref.name if !@benchmark.group[0].nil?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @attribute['content_ref.href'] = @benchmark.group[0].rule.check.content_ref.href if !@benchmark.group[0].nil?
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  @attribute
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def publisher
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @benchmark.reference.dc_publisher
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def published
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @benchmark.release_date.release_date
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def inject_metadata(metadata = '{}')
         
     | 
| 
      
 84 
     | 
    
         
            +
                  json_metadata = JSON.parse(metadata)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  json_metadata.each do |key, value|
         
     | 
| 
      
 86 
     | 
    
         
            +
                    @profile[key] = value
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                private
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                def replace_tags_in_xccdf(replace_tags, xccdf_xml)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  replace_tags.each do |tag|
         
     | 
| 
      
 94 
     | 
    
         
            +
                    xccdf_xml = xccdf_xml.gsub(/(<|<)#{tag}(>|>)/, "$#{tag}")
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  xccdf_xml
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def insert_json_metadata
         
     | 
| 
      
 100 
     | 
    
         
            +
                  @profile['name'] = @benchmark.id
         
     | 
| 
      
 101 
     | 
    
         
            +
                  @profile['title'] = @benchmark.title
         
     | 
| 
      
 102 
     | 
    
         
            +
                  @profile['maintainer'] = 'The Authors' if @profile['maintainer'].nil?
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @profile['copyright'] = 'The Authors' if @profile['copyright'].nil?
         
     | 
| 
      
 104 
     | 
    
         
            +
                  @profile['copyright_email'] = 'you@example.com' if @profile['copyright_email'].nil?
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @profile['license'] = 'Apache-2.0' if @profile['license'].nil?
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @profile['summary'] = "\"#{@benchmark.description.gsub('\\', '\\\\\\').gsub('"', '\"')}\""
         
     | 
| 
      
 107 
     | 
    
         
            +
                  @profile['version'] = '0.1.0' if @profile['version'].nil?
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @profile['supports'] = []
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @profile['attributes'] = []
         
     | 
| 
      
 110 
     | 
    
         
            +
                  @profile['generator'] = {
         
     | 
| 
      
 111 
     | 
    
         
            +
                    'name': 'inspec_tools',
         
     | 
| 
      
 112 
     | 
    
         
            +
                    'version': VERSION
         
     | 
| 
      
 113 
     | 
    
         
            +
                  }
         
     | 
| 
      
 114 
     | 
    
         
            +
                  @profile['plaintext'] = @benchmark.plaintext.plaintext
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @profile['status'] = "#{@benchmark.status} on #{@benchmark.release_date.release_date}"
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @profile['reference_href'] = @benchmark.reference.href
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @profile['reference_publisher'] = @benchmark.reference.dc_publisher
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @profile['reference_source'] = @benchmark.reference.dc_source
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def insert_controls
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @benchmark.group.each do |group|
         
     | 
| 
      
 123 
     | 
    
         
            +
                    control = {}
         
     | 
| 
      
 124 
     | 
    
         
            +
                    control['id'] = group.id
         
     | 
| 
      
 125 
     | 
    
         
            +
                    control['title'] = group.rule.title
         
     | 
| 
      
 126 
     | 
    
         
            +
                    control['desc'] = group.rule.description.vuln_discussion.split('Satisfies: ')[0]
         
     | 
| 
      
 127 
     | 
    
         
            +
                    control['impact'] = Utils::InspecUtil.get_impact(group.rule.severity)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    control['tags'] = {}
         
     | 
| 
      
 129 
     | 
    
         
            +
                    control['tags']['severity'] = Utils::InspecUtil.get_impact_string(control['impact'])
         
     | 
| 
      
 130 
     | 
    
         
            +
                    control['tags']['gtitle'] = group.title
         
     | 
| 
      
 131 
     | 
    
         
            +
                    control['tags']['satisfies'] = group.rule.description.vuln_discussion.split('Satisfies: ')[1].split(',').map(&:strip) if group.rule.description.vuln_discussion.split('Satisfies: ').length > 1
         
     | 
| 
      
 132 
     | 
    
         
            +
                    control['tags']['gid'] = group.id
         
     | 
| 
      
 133 
     | 
    
         
            +
                    control['tags']['rid'] = group.rule.id
         
     | 
| 
      
 134 
     | 
    
         
            +
                    control['tags']['stig_id'] = group.rule.version
         
     | 
| 
      
 135 
     | 
    
         
            +
                    control['tags']['fix_id'] = group.rule.fix.id
         
     | 
| 
      
 136 
     | 
    
         
            +
                    control['tags']['cci'] = group.rule.idents
         
     | 
| 
      
 137 
     | 
    
         
            +
                    control['tags']['nist'] = @cci_items.fetch_nists(group.rule.idents)
         
     | 
| 
      
 138 
     | 
    
         
            +
                    control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
         
     | 
| 
      
 139 
     | 
    
         
            +
                    control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
         
     | 
| 
      
 140 
     | 
    
         
            +
                    control['tags']['documentable'] = group.rule.description.documentable if group.rule.description.documentable != ''
         
     | 
| 
      
 141 
     | 
    
         
            +
                    control['tags']['mitigations'] = group.rule.description.false_negatives if group.rule.description.mitigations != ''
         
     | 
| 
      
 142 
     | 
    
         
            +
                    control['tags']['severity_override_guidance'] = group.rule.description.severity_override_guidance if group.rule.description.severity_override_guidance != ''
         
     | 
| 
      
 143 
     | 
    
         
            +
                    control['tags']['potential_impacts'] = group.rule.description.potential_impacts if group.rule.description.potential_impacts != ''
         
     | 
| 
      
 144 
     | 
    
         
            +
                    control['tags']['third_party_tools'] = group.rule.description.third_party_tools if group.rule.description.third_party_tools != ''
         
     | 
| 
      
 145 
     | 
    
         
            +
                    control['tags']['mitigation_controls'] = group.rule.description.mitigation_controls if group.rule.description.mitigation_controls != ''
         
     | 
| 
      
 146 
     | 
    
         
            +
                    control['tags']['responsibility'] = group.rule.description.responsibility if group.rule.description.responsibility != ''
         
     | 
| 
      
 147 
     | 
    
         
            +
                    control['tags']['ia_controls'] = group.rule.description.ia_controls if group.rule.description.ia_controls != ''
         
     | 
| 
      
 148 
     | 
    
         
            +
                    control['tags']['check'] = group.rule.check.content
         
     | 
| 
      
 149 
     | 
    
         
            +
                    control['tags']['fix'] = group.rule.fixtext
         
     | 
| 
      
 150 
     | 
    
         
            +
                    @controls << control
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                  @profile['controls'] = @controls
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
              end
         
     | 
| 
      
 155 
     | 
    
         
            +
            end
         
     |