inspec_tools 2.0.4 → 2.2.0
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 +4 -4
- data/README.md +25 -17
- data/Rakefile +82 -8
- data/lib/data/cis_to_nist_critical_controls +0 -0
- data/lib/data/cis_to_nist_mapping +0 -0
- data/lib/happy_mapper_tools/benchmark.rb +83 -0
- data/lib/happy_mapper_tools/stig_attributes.rb +68 -48
- data/lib/inspec_tools/csv.rb +42 -39
- data/lib/inspec_tools/generate_map.rb +35 -0
- data/lib/inspec_tools/inspec.rb +19 -91
- data/lib/inspec_tools/pdf.rb +2 -13
- data/lib/inspec_tools/plugin_cli.rb +22 -53
- data/lib/inspec_tools/summary.rb +108 -76
- data/lib/inspec_tools/xccdf.rb +1 -0
- data/lib/inspec_tools/xlsx_tool.rb +4 -16
- data/lib/utilities/cci_xml.rb +13 -0
- data/lib/utilities/cis_to_nist.rb +11 -0
- data/lib/utilities/inspec_util.rb +10 -74
- data/lib/utilities/mapping_validator.rb +10 -0
- data/lib/utilities/xccdf/from_inspec.rb +89 -0
- data/lib/utilities/xccdf/to_xccdf.rb +388 -0
- data/lib/utilities/xccdf/xccdf_score.rb +116 -0
- metadata +48 -29
- data/CHANGELOG.md +0 -714
- 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/utilities/extract_nist_cis_mapping.rb +0 -57
    
        data/lib/inspec_tools/csv.rb
    CHANGED
    
    | @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            require 'csv'
         | 
| 2 | 
            -
            require 'nokogiri'
         | 
| 3 | 
            -
            require 'word_wrap'
         | 
| 4 2 | 
             
            require 'yaml'
         | 
| 5 3 | 
             
            require 'digest'
         | 
| 6 4 |  | 
| 7 5 | 
             
            require_relative '../utilities/inspec_util'
         | 
| 6 | 
            +
            require_relative '../utilities/cci_xml'
         | 
| 7 | 
            +
            require_relative '../utilities/mapping_validator'
         | 
| 8 8 |  | 
| 9 9 | 
             
            # rubocop:disable Metrics/AbcSize
         | 
| 10 10 | 
             
            # rubocop:disable Metrics/PerceivedComplexity
         | 
| @@ -16,34 +16,25 @@ module InspecTools | |
| 16 16 | 
             
                def initialize(csv, mapping, name, verbose = false)
         | 
| 17 17 | 
             
                  @name = name
         | 
| 18 18 | 
             
                  @csv = csv
         | 
| 19 | 
            -
                  @mapping = mapping
         | 
| 19 | 
            +
                  @mapping = Utils::MappingValidator.validate(mapping)
         | 
| 20 20 | 
             
                  @verbose = verbose
         | 
| 21 21 | 
             
                  @csv.shift if @mapping['skip_csv_header']
         | 
| 22 22 | 
             
                end
         | 
| 23 23 |  | 
| 24 | 
            -
                def  | 
| 25 | 
            -
                  # TODO
         | 
| 26 | 
            -
                end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                def to_xccdf
         | 
| 29 | 
            -
                  # TODO
         | 
| 30 | 
            -
                end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                def to_inspec
         | 
| 24 | 
            +
                def to_inspec(control_name_prefix: nil)
         | 
| 33 25 | 
             
                  @controls = []
         | 
| 34 | 
            -
                  @cci_xml = nil
         | 
| 35 26 | 
             
                  @profile = {}
         | 
| 36 | 
            -
                   | 
| 37 | 
            -
                   | 
| 38 | 
            -
                  parse_controls
         | 
| 27 | 
            +
                  @cci_xml = Utils::CciXml.get_cci_list('U_CCI_List.xml')
         | 
| 28 | 
            +
                  insert_metadata
         | 
| 29 | 
            +
                  parse_controls(control_name_prefix)
         | 
| 39 30 | 
             
                  @profile['controls'] = @controls
         | 
| 40 | 
            -
                  @profile['sha256'] = Digest::SHA256.hexdigest | 
| 31 | 
            +
                  @profile['sha256'] = Digest::SHA256.hexdigest(@profile.to_s)
         | 
| 41 32 | 
             
                  @profile
         | 
| 42 33 | 
             
                end
         | 
| 43 34 |  | 
| 44 35 | 
             
                private
         | 
| 45 36 |  | 
| 46 | 
            -
                def  | 
| 37 | 
            +
                def insert_metadata
         | 
| 47 38 | 
             
                  @profile['name'] = @name
         | 
| 48 39 | 
             
                  @profile['title'] = 'InSpec Profile'
         | 
| 49 40 | 
             
                  @profile['maintainer'] = 'The Authors'
         | 
| @@ -60,35 +51,37 @@ module InspecTools | |
| 60 51 | 
             
                  }
         | 
| 61 52 | 
             
                end
         | 
| 62 53 |  | 
| 63 | 
            -
                def read_cci_xml
         | 
| 64 | 
            -
                  cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
         | 
| 65 | 
            -
                  @cci_xml = Nokogiri::XML(File.open(cci_list_path))
         | 
| 66 | 
            -
                  @cci_xml.remove_namespaces!
         | 
| 67 | 
            -
                rescue StandardError => e
         | 
| 68 | 
            -
                  puts "Exception: #{e.message}"
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 54 | 
             
                def get_nist_reference(cci_number)
         | 
| 72 55 | 
             
                  item_node = @cci_xml.xpath("//cci_list/cci_items/cci_item[@id='#{cci_number}']")[0] unless @cci_xml.nil?
         | 
| 73 | 
            -
                   | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
                  end
         | 
| 77 | 
            -
                  [nist_ref, nist_ver]
         | 
| 56 | 
            +
                  return nil if item_node.nil?
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  [] << item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@index').text
         | 
| 78 59 | 
             
                end
         | 
| 79 60 |  | 
| 80 | 
            -
                def  | 
| 61 | 
            +
                def get_cci_number(cell)
         | 
| 62 | 
            +
                  # Return nil if a mapping to the CCI was not provided or if there is not content in the CSV cell.
         | 
| 63 | 
            +
                  return nil if cell.nil? || @mapping['control.tags']['cci'].nil?
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  # If the content has been exported from STIG Viewer, the cell will have extra information
         | 
| 66 | 
            +
                  cell.split("\n").first
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def parse_controls(prefix)
         | 
| 81 70 | 
             
                  @csv.each do |row|
         | 
| 82 | 
            -
                    print '.'
         | 
| 83 71 | 
             
                    control = {}
         | 
| 84 | 
            -
                    control['id']     = row[@mapping['control.id']] | 
| 85 | 
            -
                    control['title']  = row[@mapping['control.title']] | 
| 86 | 
            -
                    control['desc']   = row[@mapping['control.desc']] | 
| 72 | 
            +
                    control['id']     = generate_control_id(prefix, row[@mapping['control.id']]) unless @mapping['control.id'].nil? || row[@mapping['control.id']].nil?
         | 
| 73 | 
            +
                    control['title']  = row[@mapping['control.title']] unless @mapping['control.title'].nil? || row[@mapping['control.title']].nil?
         | 
| 74 | 
            +
                    control['desc']   = row[@mapping['control.desc']] unless @mapping['control.desc'].nil? || row[@mapping['control.desc']].nil?
         | 
| 87 75 | 
             
                    control['tags'] = {}
         | 
| 88 | 
            -
                     | 
| 89 | 
            -
                     | 
| 76 | 
            +
                    cci_number = get_cci_number(row[@mapping['control.tags']['cci']])
         | 
| 77 | 
            +
                    nist = get_nist_reference(cci_number) unless cci_number.nil?
         | 
| 78 | 
            +
                    control['tags']['nist'] = nist unless nist.nil? || nist.include?(nil)
         | 
| 90 79 | 
             
                    @mapping['control.tags'].each do |tag|
         | 
| 91 | 
            -
                       | 
| 80 | 
            +
                      if tag.first == 'cci'
         | 
| 81 | 
            +
                        control['tags'][tag.first] = cci_number
         | 
| 82 | 
            +
                        next
         | 
| 83 | 
            +
                      end
         | 
| 84 | 
            +
                      control['tags'][tag.first] = row[tag.last] unless row[tag.last].nil?
         | 
| 92 85 | 
             
                    end
         | 
| 93 86 | 
             
                    unless @mapping['control.tags']['severity'].nil? || row[@mapping['control.tags']['severity']].nil?
         | 
| 94 87 | 
             
                      control['impact'] = Utils::InspecUtil.get_impact(row[@mapping['control.tags']['severity']])
         | 
| @@ -97,5 +90,15 @@ module InspecTools | |
| 97 90 | 
             
                    @controls << control
         | 
| 98 91 | 
             
                  end
         | 
| 99 92 | 
             
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def generate_control_id(prefix, id)
         | 
| 95 | 
            +
                  return id if prefix.nil?
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  "#{prefix}-#{id}"
         | 
| 98 | 
            +
                end
         | 
| 100 99 | 
             
              end
         | 
| 101 100 | 
             
            end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            # rubocop:enable Metrics/AbcSize
         | 
| 103 | 
            +
            # rubocop:enable Metrics/PerceivedComplexity
         | 
| 104 | 
            +
            # rubocop:enable Metrics/CyclomaticComplexity
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module InspecTools
         | 
| 2 | 
            +
              class GenerateMap
         | 
| 3 | 
            +
                attr_accessor :text
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(text = nil)
         | 
| 6 | 
            +
                  @text = text.nil? ? default_text : text
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def generate_example(file)
         | 
| 10 | 
            +
                  File.write(file, @text)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                private
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def default_text
         | 
| 16 | 
            +
                  <<~YML
         | 
| 17 | 
            +
                  # Setting csv_header to true will skip the csv file header
         | 
| 18 | 
            +
                  skip_csv_header: true
         | 
| 19 | 
            +
                  width   : 80
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
                  control.id: 0
         | 
| 23 | 
            +
                  control.title: 15
         | 
| 24 | 
            +
                  control.desc: 16
         | 
| 25 | 
            +
                  control.tags:
         | 
| 26 | 
            +
                    severity: 1
         | 
| 27 | 
            +
                    rid: 8
         | 
| 28 | 
            +
                    stig_id: 3
         | 
| 29 | 
            +
                    cci: 2
         | 
| 30 | 
            +
                    check: 12
         | 
| 31 | 
            +
                    fix: 10
         | 
| 32 | 
            +
                  YML
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/lib/inspec_tools/inspec.rb
    CHANGED
    
    | @@ -9,17 +9,14 @@ require_relative '../happy_mapper_tools/stig_checklist' | |
| 9 9 | 
             
            require_relative '../happy_mapper_tools/benchmark'
         | 
| 10 10 | 
             
            require_relative '../utilities/inspec_util'
         | 
| 11 11 | 
             
            require_relative 'csv'
         | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
            # rubocop:disable Metrics/AbcSize
         | 
| 15 | 
            -
            # rubocop:disable Metrics/BlockLength
         | 
| 16 | 
            -
            # rubocop:disable Style/GuardClause
         | 
| 12 | 
            +
            require_relative '../utilities/xccdf/from_inspec'
         | 
| 13 | 
            +
            require_relative '../utilities/xccdf/to_xccdf'
         | 
| 17 14 |  | 
| 18 15 | 
             
            module InspecTools
         | 
| 19 | 
            -
              class Inspec
         | 
| 20 | 
            -
                def initialize(inspec_json, metadata =  | 
| 16 | 
            +
              class Inspec # rubocop:disable Metrics/ClassLength
         | 
| 17 | 
            +
                def initialize(inspec_json, metadata = {})
         | 
| 21 18 | 
             
                  @json = JSON.parse(inspec_json.gsub(/\\+u0000/, ''))
         | 
| 22 | 
            -
                  @metadata =  | 
| 19 | 
            +
                  @metadata = metadata
         | 
| 23 20 | 
             
                end
         | 
| 24 21 |  | 
| 25 22 | 
             
                def to_ckl(title = nil, date = nil, cklist = nil)
         | 
| @@ -36,16 +33,15 @@ module InspecTools | |
| 36 33 | 
             
                  @checklist.to_xml.encode('UTF-8').gsub('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>').chomp
         | 
| 37 34 | 
             
                end
         | 
| 38 35 |  | 
| 36 | 
            +
                # Convert Inspec result data to XCCDF
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                # @param attributes [Hash] Optional input attributes
         | 
| 39 | 
            +
                # @return [String] XML formatted String
         | 
| 39 40 | 
             
                def to_xccdf(attributes, verbose = false)
         | 
| 40 | 
            -
                   | 
| 41 | 
            -
                  @attribute = attributes
         | 
| 42 | 
            -
                  @attribute = {} if @attribute.eql? false
         | 
| 41 | 
            +
                  data = Utils::FromInspec.new.parse_data_for_xccdf(@json)
         | 
| 43 42 | 
             
                  @verbose = verbose
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                   | 
| 46 | 
            -
                  # populate_profiles @todo populate profiles; not implemented now because its use is deprecated
         | 
| 47 | 
            -
                  populate_groups
         | 
| 48 | 
            -
                  @benchmark.to_xml
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  Utils::ToXCCDF.new(attributes || {}, data).to_xml(@metadata)
         | 
| 49 45 | 
             
                end
         | 
| 50 46 |  | 
| 51 47 | 
             
                ####
         | 
| @@ -70,7 +66,7 @@ module InspecTools | |
| 70 66 | 
             
                #
         | 
| 71 67 | 
             
                # @param inspec_json : an inspec profile formatted as a json object
         | 
| 72 68 | 
             
                ###
         | 
| 73 | 
            -
                def inspec_json_to_array(inspec_json)
         | 
| 69 | 
            +
                def inspec_json_to_array(inspec_json) # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 74 70 | 
             
                  data = []
         | 
| 75 71 | 
             
                  headers = {}
         | 
| 76 72 | 
             
                  inspec_json['controls'].each do |control|
         | 
| @@ -97,10 +93,11 @@ module InspecTools | |
| 97 93 | 
             
                      @data['controls'] << control
         | 
| 98 94 | 
             
                    end
         | 
| 99 95 | 
             
                  end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 96 | 
            +
             | 
| 97 | 
            +
                  return unless json['profiles'].nil?
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  json['controls'].each do |control|
         | 
| 100 | 
            +
                    @data['controls'] << control
         | 
| 104 101 | 
             
                  end
         | 
| 105 102 | 
             
                end
         | 
| 106 103 |  | 
| @@ -161,7 +158,7 @@ module InspecTools | |
| 161 158 | 
             
                  vuln
         | 
| 162 159 | 
             
                end
         | 
| 163 160 |  | 
| 164 | 
            -
                def generate_asset
         | 
| 161 | 
            +
                def generate_asset # rubocop:disable Metrics/AbcSize
         | 
| 165 162 | 
             
                  asset = HappyMapperTools::StigChecklist::Asset.new
         | 
| 166 163 | 
             
                  asset.role = !@metadata['role'].nil? ? @metadata['role'] : 'Workstation'
         | 
| 167 164 | 
             
                  asset.type = !@metadata['type'].nil? ? @metadata['type'] : 'Computing'
         | 
| @@ -223,75 +220,6 @@ module InspecTools | |
| 223 220 | 
             
                  ip
         | 
| 224 221 | 
             
                end
         | 
| 225 222 |  | 
| 226 | 
            -
                def populate_header
         | 
| 227 | 
            -
                  @benchmark.title = @attribute['benchmark.title']
         | 
| 228 | 
            -
                  @benchmark.id = @attribute['benchmark.id']
         | 
| 229 | 
            -
                  @benchmark.description = @attribute['benchmark.description']
         | 
| 230 | 
            -
                  @benchmark.version = @attribute['benchmark.version']
         | 
| 231 | 
            -
             | 
| 232 | 
            -
                  @benchmark.status = HappyMapperTools::Benchmark::Status.new
         | 
| 233 | 
            -
                  @benchmark.status.status = @attribute['benchmark.status']
         | 
| 234 | 
            -
                  @benchmark.status.date = @attribute['benchmark.status.date']
         | 
| 235 | 
            -
             | 
| 236 | 
            -
                  @benchmark.notice = HappyMapperTools::Benchmark::Notice.new
         | 
| 237 | 
            -
                  @benchmark.notice.id = @attribute['benchmark.notice.id']
         | 
| 238 | 
            -
             | 
| 239 | 
            -
                  @benchmark.plaintext = HappyMapperTools::Benchmark::Plaintext.new
         | 
| 240 | 
            -
                  @benchmark.plaintext.plaintext = @attribute['benchmark.plaintext']
         | 
| 241 | 
            -
                  @benchmark.plaintext.id = @attribute['benchmark.plaintext.id']
         | 
| 242 | 
            -
             | 
| 243 | 
            -
                  @benchmark.reference = HappyMapperTools::Benchmark::ReferenceBenchmark.new
         | 
| 244 | 
            -
                  @benchmark.reference.href = @attribute['reference.href']
         | 
| 245 | 
            -
                  @benchmark.reference.dc_publisher = @attribute['reference.dc.publisher']
         | 
| 246 | 
            -
                  @benchmark.reference.dc_source = @attribute['reference.dc.source']
         | 
| 247 | 
            -
                end
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                def populate_groups
         | 
| 250 | 
            -
                  group_array = []
         | 
| 251 | 
            -
                  @data['controls'].each do |control|
         | 
| 252 | 
            -
                    group = HappyMapperTools::Benchmark::Group.new
         | 
| 253 | 
            -
                    group.id = control['id']
         | 
| 254 | 
            -
                    group.title = control['gtitle']
         | 
| 255 | 
            -
                    group.description = "<GroupDescription>#{control['gdescription']}</GroupDescription>"
         | 
| 256 | 
            -
             | 
| 257 | 
            -
                    group.rule = HappyMapperTools::Benchmark::Rule.new
         | 
| 258 | 
            -
                    group.rule.id = control['rid']
         | 
| 259 | 
            -
                    group.rule.severity = control['severity']
         | 
| 260 | 
            -
                    group.rule.weight = control['rweight']
         | 
| 261 | 
            -
                    group.rule.version = control['rversion']
         | 
| 262 | 
            -
                    group.rule.title = control['title'].tr("\n", ' ')
         | 
| 263 | 
            -
                    group.rule.description = "<VulnDiscussion>#{control['desc'].tr("\n", ' ')}</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls>"
         | 
| 264 | 
            -
             | 
| 265 | 
            -
                    group.rule.reference = HappyMapperTools::Benchmark::ReferenceGroup.new
         | 
| 266 | 
            -
                    group.rule.reference.dc_publisher = @attribute['reference.dc.publisher']
         | 
| 267 | 
            -
                    group.rule.reference.dc_title = @attribute['reference.dc.title']
         | 
| 268 | 
            -
                    group.rule.reference.dc_subject = @attribute['reference.dc.subject']
         | 
| 269 | 
            -
                    group.rule.reference.dc_type = @attribute['reference.dc.type']
         | 
| 270 | 
            -
                    group.rule.reference.dc_identifier = @attribute['reference.dc.identifier']
         | 
| 271 | 
            -
             | 
| 272 | 
            -
                    group.rule.ident = HappyMapperTools::Benchmark::Ident.new
         | 
| 273 | 
            -
                    group.rule.ident.system = 'https://public.cyber.mil/stigs/cci/'
         | 
| 274 | 
            -
                    group.rule.ident.ident = control['cci']
         | 
| 275 | 
            -
             | 
| 276 | 
            -
                    group.rule.fixtext = HappyMapperTools::Benchmark::Fixtext.new
         | 
| 277 | 
            -
                    group.rule.fixtext.fixref = control['fixref']
         | 
| 278 | 
            -
                    group.rule.fixtext.fixtext = control['fix']
         | 
| 279 | 
            -
             | 
| 280 | 
            -
                    group.rule.fix = HappyMapperTools::Benchmark::Fix.new
         | 
| 281 | 
            -
                    group.rule.fix.id = control['fixref']
         | 
| 282 | 
            -
             | 
| 283 | 
            -
                    group.rule.check = HappyMapperTools::Benchmark::Check.new
         | 
| 284 | 
            -
                    group.rule.check.system = control['checkref']
         | 
| 285 | 
            -
                    group.rule.check.content_ref = HappyMapperTools::Benchmark::ContentRef.new
         | 
| 286 | 
            -
                    group.rule.check.content_ref.name = @attribute['content_ref.name']
         | 
| 287 | 
            -
                    group.rule.check.content_ref.href = @attribute['content_ref.href']
         | 
| 288 | 
            -
                    group.rule.check.content = control['check']
         | 
| 289 | 
            -
             | 
| 290 | 
            -
                    group_array << group
         | 
| 291 | 
            -
                  end
         | 
| 292 | 
            -
                  @benchmark.group = group_array
         | 
| 293 | 
            -
                end
         | 
| 294 | 
            -
             | 
| 295 223 | 
             
                def generate_title(title, json, date)
         | 
| 296 224 | 
             
                  title ||= "Untitled - Checklist Created from Automated InSpec Results JSON; Profiles: #{json['profiles'].map { |x| x['name'] }.join(' | ')}"
         | 
| 297 225 | 
             
                  title + " Checklist Date: #{date || Date.today.to_s}"
         | 
    
        data/lib/inspec_tools/pdf.rb
    CHANGED
    
    | @@ -2,9 +2,9 @@ require 'digest' | |
| 2 2 |  | 
| 3 3 | 
             
            require_relative '../utilities/inspec_util'
         | 
| 4 4 | 
             
            require_relative '../utilities/extract_pdf_text'
         | 
| 5 | 
            -
            require_relative '../utilities/extract_nist_cis_mapping'
         | 
| 6 5 | 
             
            require_relative '../utilities/parser'
         | 
| 7 6 | 
             
            require_relative '../utilities/text_cleaner'
         | 
| 7 | 
            +
            require_relative '../utilities/cis_to_nist'
         | 
| 8 8 |  | 
| 9 9 | 
             
            # rubocop:disable Metrics/AbcSize
         | 
| 10 10 | 
             
            # rubocop:disable Metrics/PerceivedComplexity
         | 
| @@ -24,7 +24,7 @@ module InspecTools | |
| 24 24 | 
             
                  @controls = []
         | 
| 25 25 | 
             
                  @csv_handle = nil
         | 
| 26 26 | 
             
                  @cci_xml = nil
         | 
| 27 | 
            -
                  @nist_mapping =  | 
| 27 | 
            +
                  @nist_mapping = Utils::CisToNist.get_mapping('cis_to_nist_critical_controls')
         | 
| 28 28 | 
             
                  @pdf_text = ''
         | 
| 29 29 | 
             
                  @clean_text = ''
         | 
| 30 30 | 
             
                  @transformed_data = ''
         | 
| @@ -33,7 +33,6 @@ module InspecTools | |
| 33 33 | 
             
                  @title ||= extract_title
         | 
| 34 34 | 
             
                  clean_pdf_text
         | 
| 35 35 | 
             
                  transform_data
         | 
| 36 | 
            -
                  read_excl
         | 
| 37 36 | 
             
                  insert_json_metadata
         | 
| 38 37 | 
             
                  @profile['controls'] = parse_controls
         | 
| 39 38 | 
             
                  @profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
         | 
| @@ -122,15 +121,5 @@ module InspecTools | |
| 122 121 | 
             
                def write_clean_text
         | 
| 123 122 | 
             
                  File.write('debug_text', @clean_text)
         | 
| 124 123 | 
             
                end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                def read_excl
         | 
| 127 | 
            -
                  nist_map_path = File.join(File.dirname(__FILE__), '../data/NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx')
         | 
| 128 | 
            -
                  excel = Util::ExtractNistMappings.new(nist_map_path)
         | 
| 129 | 
            -
                  @nist_mapping = excel.full_excl
         | 
| 130 | 
            -
                rescue StandardError => e
         | 
| 131 | 
            -
                  puts "Exception: #{e.message}"
         | 
| 132 | 
            -
                  puts 'Existing...'
         | 
| 133 | 
            -
                  exit
         | 
| 134 | 
            -
                end
         | 
| 135 124 | 
             
              end
         | 
| 136 125 | 
             
            end
         | 
| @@ -1,4 +1,3 @@ | |
| 1 | 
            -
            require 'yaml'
         | 
| 2 1 | 
             
            require 'json'
         | 
| 3 2 | 
             
            require 'roo'
         | 
| 4 3 | 
             
            require_relative '../utilities/inspec_util'
         | 
| @@ -13,8 +12,8 @@ module InspecTools | |
| 13 12 | 
             
              autoload :CKL, 'inspec_tools/ckl'
         | 
| 14 13 | 
             
              autoload :Inspec, 'inspec_tools/inspec'
         | 
| 15 14 | 
             
              autoload :Summary, 'inspec_tools/summary'
         | 
| 16 | 
            -
              autoload :Threshold, 'inspec_tools/threshold'
         | 
| 17 15 | 
             
              autoload :XLSXTool, 'inspec_tools/xlsx_tool'
         | 
| 16 | 
            +
              autoload :GenerateMap, 'inspec_tools/generate_map'
         | 
| 18 17 | 
             
            end
         | 
| 19 18 |  | 
| 20 19 | 
             
            # rubocop:disable Style/GuardClause
         | 
| @@ -23,7 +22,7 @@ module InspecPlugins | |
| 23 22 | 
             
                class CliCommand < Inspec.plugin(2, :cli_command) # rubocop:disable Metrics/ClassLength
         | 
| 24 23 | 
             
                  POSSIBLE_LOG_LEVELS = %w{debug info warn error fatal}.freeze
         | 
| 25 24 |  | 
| 26 | 
            -
                  class_option :log_directory, type: :string, aliases: :l, desc: ' | 
| 25 | 
            +
                  class_option :log_directory, type: :string, aliases: :l, desc: 'Provide log location'
         | 
| 27 26 | 
             
                  class_option :log_level, type: :string, desc: "Set the logging level: #{POSSIBLE_LOG_LEVELS}"
         | 
| 28 27 |  | 
| 29 28 | 
             
                  subcommand_desc 'tools [COMMAND]', 'Runs inspec_tools commands through Inspec'
         | 
| @@ -54,12 +53,18 @@ module InspecPlugins | |
| 54 53 |  | 
| 55 54 | 
             
                  desc 'inspec2xccdf', 'inspec2xccdf translates an inspec profile and attributes files to an xccdf file'
         | 
| 56 55 | 
             
                  long_desc InspecTools::Help.text(:inspec2xccdf)
         | 
| 57 | 
            -
                  option :inspec_json, required: true, aliases: '-j'
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                  option : | 
| 56 | 
            +
                  option :inspec_json, required: true, aliases: '-j',
         | 
| 57 | 
            +
                                       desc: 'path to InSpec JSON file created'
         | 
| 58 | 
            +
                  option :attributes,  required: true, aliases: '-a',
         | 
| 59 | 
            +
                                       desc: 'path to yml file that provides the required attributes for the XCCDF document. These attributes are parts of XCCDF document which do not fit into the InSpec schema.'
         | 
| 60 | 
            +
                  option :output, required: true, aliases: '-o',
         | 
| 61 | 
            +
                                  desc: 'name or path to create the XCCDF and title to give the XCCDF'
         | 
| 62 | 
            +
                  option :metadata, required: false, type: :string, aliases: '-m',
         | 
| 63 | 
            +
                                    desc: 'path to JSON file with additional host metadata for the XCCDF file'
         | 
| 60 64 | 
             
                  def inspec2xccdf
         | 
| 61 65 | 
             
                    json = File.read(options[:inspec_json])
         | 
| 62 | 
            -
                     | 
| 66 | 
            +
                    metadata = options[:metadata] ? JSON.parse(File.read(options[:metadata])) : {}
         | 
| 67 | 
            +
                    inspec_tool = InspecTools::Inspec.new(json, metadata)
         | 
| 63 68 | 
             
                    attr_hsh = YAML.load_file(options[:attributes])
         | 
| 64 69 | 
             
                    xccdf = inspec_tool.to_xccdf(attr_hsh)
         | 
| 65 70 | 
             
                    File.write(options[:output], xccdf)
         | 
| @@ -73,10 +78,11 @@ module InspecPlugins | |
| 73 78 | 
             
                  option :output, required: false, aliases: '-o', default: 'profile'
         | 
| 74 79 | 
             
                  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
         | 
| 75 80 | 
             
                  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
         | 
| 81 | 
            +
                  option :control_name_prefix, required: false, type: :string, aliases: '-p'
         | 
| 76 82 | 
             
                  def csv2inspec
         | 
| 77 83 | 
             
                    csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
         | 
| 78 84 | 
             
                    mapping = YAML.load_file(options[:mapping])
         | 
| 79 | 
            -
                    profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
         | 
| 85 | 
            +
                    profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec(control_name_prefix: options[:control_name_prefix])
         | 
| 80 86 | 
             
                    Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
         | 
| 81 87 | 
             
                  end
         | 
| 82 88 |  | 
| @@ -136,26 +142,8 @@ module InspecPlugins | |
| 136 142 |  | 
| 137 143 | 
             
                  desc 'generate_map', 'Generates mapping template from CSV to Inspec Controls'
         | 
| 138 144 | 
             
                  def generate_map
         | 
| 139 | 
            -
                     | 
| 140 | 
            -
                     | 
| 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
         | 
| 145 | 
            +
                    generator = InspecTools::GenerateMap.new
         | 
| 146 | 
            +
                    generator.generate_example('mapping.yml')
         | 
| 159 147 | 
             
                  end
         | 
| 160 148 |  | 
| 161 149 | 
             
                  desc 'generate_ckl_metadata', 'Generate metadata file that can be passed to inspec2ckl'
         | 
| @@ -200,26 +188,14 @@ module InspecPlugins | |
| 200 188 | 
             
                  desc 'summary', 'summary parses an inspec results json to create a summary json'
         | 
| 201 189 | 
             
                  long_desc InspecTools::Help.text(:summary)
         | 
| 202 190 | 
             
                  option :inspec_json, required: true, aliases: '-j'
         | 
| 203 | 
            -
                  option :verbose, type: :boolean, aliases: '-V'
         | 
| 204 191 | 
             
                  option :json_full, type: :boolean, required: false, aliases: '-f'
         | 
| 205 192 | 
             
                  option :json_counts, type: :boolean, required: false, aliases: '-k'
         | 
| 193 | 
            +
                  option :threshold_file, required: false, aliases: '-t'
         | 
| 194 | 
            +
                  option :threshold_inline, required: false, aliases: '-i'
         | 
| 206 195 |  | 
| 207 196 | 
             
                  def summary
         | 
| 208 | 
            -
                    summary = InspecTools::Summary.new( | 
| 209 | 
            -
             | 
| 210 | 
            -
                    unless options.include?('json_full') || options.include?('json_counts')
         | 
| 211 | 
            -
                      puts "\nOverall compliance: #{summary[:compliance]}%\n\n"
         | 
| 212 | 
            -
                      summary[:status].keys.each do |category|
         | 
| 213 | 
            -
                        puts category
         | 
| 214 | 
            -
                        summary[:status][category].keys.each do |impact|
         | 
| 215 | 
            -
                          puts "\t#{impact} : #{summary[:status][category][impact]}"
         | 
| 216 | 
            -
                        end
         | 
| 217 | 
            -
                      end
         | 
| 218 | 
            -
                    end
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                    json_summary = summary.to_json
         | 
| 221 | 
            -
                    puts json_summary if options[:json_full]
         | 
| 222 | 
            -
                    puts summary[:status].to_json if options[:json_counts]
         | 
| 197 | 
            +
                    summary = InspecTools::Summary.new(options: options)
         | 
| 198 | 
            +
                    summary.output_summary
         | 
| 223 199 | 
             
                  end
         | 
| 224 200 |  | 
| 225 201 | 
             
                  desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
         | 
| @@ -227,17 +203,10 @@ module InspecPlugins | |
| 227 203 | 
             
                  option :inspec_json, required: true, aliases: '-j'
         | 
| 228 204 | 
             
                  option :threshold_file, required: false, aliases: '-f'
         | 
| 229 205 | 
             
                  option :threshold_inline, required: false, aliases: '-i'
         | 
| 230 | 
            -
                  option :verbose, type: :boolean, aliases: '-V'
         | 
| 231 206 |  | 
| 232 207 | 
             
                  def compliance
         | 
| 233 | 
            -
                     | 
| 234 | 
            -
             | 
| 235 | 
            -
                      exit(1)
         | 
| 236 | 
            -
                    end
         | 
| 237 | 
            -
                    threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
         | 
| 238 | 
            -
                    threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
         | 
| 239 | 
            -
                    compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
         | 
| 240 | 
            -
                    compliance ? exit(0) : exit(1)
         | 
| 208 | 
            +
                    compliance = InspecTools::Summary.new(options: options)
         | 
| 209 | 
            +
                    compliance.results_meet_threshold? ? exit(0) : exit(1)
         | 
| 241 210 | 
             
                  end
         | 
| 242 211 | 
             
                end
         | 
| 243 212 | 
             
              end
         |