require 'date' require 'json' require 'cgi' require 'csv' require 'yaml' require 'pp' require_relative '../happy_mapper_tools/stig_attributes' require_relative '../happy_mapper_tools/stig_checklist' require_relative '../happy_mapper_tools/benchmark' require_relative '../utilities/inspec_util' require_relative 'csv' require_relative '../utilities/xccdf/from_inspec' require_relative '../utilities/xccdf/to_xccdf' module InspecTools class Inspec def initialize(inspec_json, metadata = {}) @json = JSON.parse(inspec_json.gsub(/\\+u0000/, '')) @metadata = metadata end def to_ckl(title = nil, date = nil, cklist = nil) @data = Utils::InspecUtil.parse_data_for_ckl(@json) @platform = Utils::InspecUtil.get_platform(@json) @title = generate_title title, @json, date @cklist = cklist @checklist = HappyMapperTools::StigChecklist::Checklist.new if @cklist.nil? generate_ckl else update_ckl end @checklist.to_xml.encode('UTF-8').gsub('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>').chomp end # Convert Inspec result data to XCCDF # # @param attributes [Hash] Optional input attributes # @return [String] XML formatted String def to_xccdf(attributes, verbose = false) data = Utils::FromInspec.new.parse_data_for_xccdf(@json) @verbose = verbose Utils::ToXCCDF.new(attributes || {}, data).to_xml(@metadata) end #### # converts an InSpec JSON to a CSV file ### def to_csv @data = {} @data['controls'] = [] get_all_controls_from_json(@json) data = inspec_json_to_array(@data) CSV.generate do |csv| data.each do |row| csv << row end end end private ### # This method converts an inspec json to an array of arrays # # @param inspec_json : an inspec profile formatted as a json object ### def inspec_json_to_array(inspec_json) data = [] headers = {} inspec_json['controls'].each do |control| control.each do |key, _| control['tags'].each { |tag, _| headers[tag] = 0 } if key == 'tags' control['results'].each { |result| result.each { |result_key, _| headers[result_key] = 0 } } if key == 'results' headers[key] = 0 unless %w{tags results}.include?(key) end end data.push(headers.keys) inspec_json['controls'].each do |json_control| control = [] headers.each do |key, _| control.push(json_control[key] || json_control['tags'][key] || json_control['results']&.collect { |result| result[key] }&.join(",\n") || nil) end data.push(control) end data end def get_all_controls_from_json(json) json['profiles']&.each do |profile| profile['controls'].each do |control| @data['controls'] << control end end return unless json['profiles'].nil? json['controls'].each do |control| @data['controls'] << control end end def update_ckl @checklist = HappyMapperTools::StigChecklist::Checklist.parse(@cklist.to_s) @data.keys.each do |control_id| vuln = @checklist.where('Vuln_Num', control_id.to_s) vuln.status = Utils::InspecUtil.control_status(@data[control_id]) vuln.finding_details << Utils::InspecUtil.control_finding_details(@data[control_id], vuln.status) end end def generate_ckl stigs = HappyMapperTools::StigChecklist::Stigs.new istig = HappyMapperTools::StigChecklist::IStig.new vuln_list = [] @data.keys.each do |control_id| vuln_list.push(generate_vuln_data(@data[control_id])) end si_data = HappyMapperTools::StigChecklist::SiData.new si_data.name = 'stigid' si_data.data = '' if !@metadata['stigid'].nil? si_data.data = @metadata['stigid'] end stig_info = HappyMapperTools::StigChecklist::StigInfo.new stig_info.si_data = si_data istig.stig_info = stig_info istig.vuln = vuln_list stigs.istig = istig @checklist.stig = stigs @checklist.asset = generate_asset end def generate_vuln_data(control) vuln = HappyMapperTools::StigChecklist::Vuln.new stig_data_list = [] %w{Vuln_Num Group_Title Rule_ID Rule_Ver Rule_Title Vuln_Discuss Check_Content Fix_Text}.each do |attribute| stig_data_list << create_stig_data_element(attribute, control) end stig_data_list << handle_severity(control) stig_data_list += handle_cci_ref(control) stig_data_list << handle_stigref vuln.stig_data = stig_data_list.reject(&:nil?) vuln.status = Utils::InspecUtil.control_status(control) vuln.comments = "\nAutomated compliance tests brought to you by the MITRE corporation and the InSpec project.\n\nInspec Profile: #{control[:profile_name]}\nProfile shasum: #{control[:profile_shasum]}" vuln.finding_details = Utils::InspecUtil.control_finding_details(control, vuln.status) vuln.severity_override = '' vuln.severity_justification = '' vuln end def generate_asset asset = HappyMapperTools::StigChecklist::Asset.new asset.role = @metadata['role'].nil? ? 'Workstation' : @metadata['role'] asset.type = @metadata['type'].nil? ? 'Computing' : @metadata['type'] asset.host_name = generate_hostname asset.host_ip = generate_ip asset.host_mac = generate_mac asset.host_fqdn = generate_fqdn asset.tech_area = @metadata['tech_area'].nil? ? '' : @metadata['tech_area'] asset.target_key = @metadata['target_key'].nil? ? '' : @metadata['target_key'] asset.web_or_database = @metadata['web_or_database'].nil? ? '0' : @metadata['web_or_database'] asset.web_db_site = @metadata['web_db_site'].nil? ? '' : @metadata['web_db_site'] asset.web_db_instance = @metadata['web_db_instance'].nil? ? '' : @metadata['web_db_instance'] asset end def generate_hostname hostname = @metadata['hostname'] if hostname.nil? && @platform.nil? hostname = '' elsif hostname.nil? hostname = @platform[:hostname] end hostname end def generate_mac mac = @metadata['mac'] if mac.nil? nics = @platform.nil? ? [] : @platform[:network] nics_macs = [] nics.each do |nic| nics_macs.push(nic[:mac]) end mac = nics_macs.join(',') end mac end def generate_fqdn fqdn = @metadata['fqdn'] if fqdn.nil? && @platform.nil? fqdn = '' elsif fqdn.nil? fqdn = @platform[:fqdn] end fqdn end def generate_ip ip = @metadata['ip'] if ip.nil? nics = @platform.nil? ? [] : @platform[:network] nics_ips = [] nics.each do |nic| nics_ips.push(*nic[:ip]) end ip = nics_ips.join(',') end ip end def generate_title(title, json, date) title ||= "Untitled - Checklist Created from Automated InSpec Results JSON; Profiles: #{json['profiles'].map { |x| x['name'] }.join(' | ')}" title + " Checklist Date: #{date || Date.today.to_s}" end def create_stig_data_element(attribute, control) return HappyMapperTools::StigChecklist::StigData.new(attribute, control[attribute.downcase.to_sym]) unless control[attribute.downcase.to_sym].nil? end def handle_severity(control) return if control[:impact].nil? value = Utils::InspecUtil.get_impact_string(control[:impact], use_cvss_terms: false) return if value == 'none' HappyMapperTools::StigChecklist::StigData.new('Severity', value) end def handle_cci_ref(control) return [] if control[:cci_ref].nil? cci_data = [] if control[:cci_ref].respond_to?(:each) control[:cci_ref].each do |cci_number| cci_data << HappyMapperTools::StigChecklist::StigData.new('CCI_REF', cci_number) end cci_data else cci_data << HappyMapperTools::StigChecklist::StigData.new('CCI_REF', control[:cci_ref]) end end def handle_stigref HappyMapperTools::StigChecklist::StigData.new('STIGRef', @title) end end end