inspec-core 5.22.3 → 5.22.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -19
- data/inspec-core.gemspec +6 -4
- data/lib/inspec/cli.rb +22 -5
- data/lib/inspec/config.rb +32 -10
- data/lib/inspec/fetcher/git.rb +43 -28
- data/lib/inspec/formatters/base.rb +1 -1
- data/lib/inspec/profile.rb +340 -18
- data/lib/inspec/resources/host.rb +4 -16
- data/lib/inspec/resources/security_policy.rb +7 -2
- data/lib/inspec/rule.rb +5 -0
- data/lib/inspec/utils/profile_ast_helpers.rb +372 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec/waiver_file_reader.rb +5 -3
- data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +2 -2
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +7 -6
- data/lib/plugins/inspec-reporter-html2/templates/default.js +6 -6
- metadata +11 -10
data/lib/inspec/profile.rb
CHANGED
@@ -15,6 +15,7 @@ require "inspec/dependencies/dependency_set"
|
|
15
15
|
require "inspec/utils/json_profile_summary"
|
16
16
|
require "inspec/dependency_loader"
|
17
17
|
require "inspec/dependency_installer"
|
18
|
+
require "inspec/utils/profile_ast_helpers"
|
18
19
|
|
19
20
|
module Inspec
|
20
21
|
class Profile
|
@@ -514,6 +515,135 @@ module Inspec
|
|
514
515
|
res
|
515
516
|
end
|
516
517
|
|
518
|
+
# Return data like profile.info(params), but try to do so without evaluating the profile.
|
519
|
+
def info_from_parse(include_tests: false)
|
520
|
+
return @info_from_parse unless @info_from_parse.nil?
|
521
|
+
|
522
|
+
@info_from_parse = {
|
523
|
+
controls: [],
|
524
|
+
groups: [],
|
525
|
+
}
|
526
|
+
|
527
|
+
# TODO - look at the various source contents
|
528
|
+
# PASS 1: parse them using rubocop-ast
|
529
|
+
# Look for controls, top-level metadata, and inputs
|
530
|
+
# PASS 2: Using the control IDs, deterimine the extents -
|
531
|
+
# line locations - of the coontrol IDs in each file, and
|
532
|
+
# then extract each source code block. Use this to populate the source code
|
533
|
+
# locations and 'code' properties.
|
534
|
+
|
535
|
+
# TODO: Verify that it doesn't do evaluation (ideally shouldn't because it is reading simply yaml file)
|
536
|
+
@info_from_parse = @info_from_parse.merge(metadata.params)
|
537
|
+
|
538
|
+
inputs_hash = {}
|
539
|
+
# Note: This only handles the case when inputs are defined in metadata file
|
540
|
+
if @profile_id.nil?
|
541
|
+
# identifying inputs using profile name
|
542
|
+
inputs_hash = Inspec::InputRegistry.list_inputs_for_profile(@info_from_parse[:name])
|
543
|
+
else
|
544
|
+
inputs_hash = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
|
545
|
+
end
|
546
|
+
|
547
|
+
# TODO: Verify if I need to do the below conversion for inputs to array
|
548
|
+
if inputs_hash.nil? || inputs_hash.empty?
|
549
|
+
# convert to array for backwards compatability
|
550
|
+
@info_from_parse[:inputs] = []
|
551
|
+
else
|
552
|
+
@info_from_parse[:inputs] = inputs_hash.values.map(&:to_hash)
|
553
|
+
end
|
554
|
+
|
555
|
+
@info_from_parse[:sha256] = sha256
|
556
|
+
|
557
|
+
# Populate :status and :status_message
|
558
|
+
if supports_platform?
|
559
|
+
@info_from_parse[:status_message] = @status_message || ""
|
560
|
+
@info_from_parse[:status] = failed? ? "failed" : "loaded"
|
561
|
+
else
|
562
|
+
@info_from_parse[:status] = "skipped"
|
563
|
+
msg = "Skipping profile: '#{name}' on unsupported platform: '#{backend.platform.name}/#{backend.platform.release}'."
|
564
|
+
@info_from_parse[:status_message] = msg
|
565
|
+
end
|
566
|
+
|
567
|
+
# @source_reader.tests contains a hash mapping control filenames to control file contents
|
568
|
+
@source_reader.tests.each do |control_filename, control_file_source|
|
569
|
+
# Parse the source code
|
570
|
+
src = RuboCop::AST::ProcessedSource.new(control_file_source, RUBY_VERSION.to_f)
|
571
|
+
source_location_ref = @source_reader.target.abs_path(control_filename)
|
572
|
+
|
573
|
+
input_collector = Inspec::Profile::AstHelper::InputCollectorOutsideControlBlock.new(@info_from_parse)
|
574
|
+
ctl_id_collector = Inspec::Profile::AstHelper::ControlIDCollector.new(@info_from_parse, source_location_ref,
|
575
|
+
include_tests: include_tests)
|
576
|
+
|
577
|
+
# Collect all metadata defined in the control block and inputs defined inside the control block
|
578
|
+
src.ast.each_node { |n|
|
579
|
+
ctl_id_collector.process(n)
|
580
|
+
input_collector.process(n)
|
581
|
+
}
|
582
|
+
|
583
|
+
# For each control ID
|
584
|
+
# Look for per-control metadata
|
585
|
+
# Filter controls by --controls, list of controls to include is available in include_controls_list
|
586
|
+
|
587
|
+
# NOTE: This is a hack to duplicate refs.
|
588
|
+
# TODO: Fix this in the ref collector or the way we traverse the AST
|
589
|
+
@info_from_parse[:controls].each { |control| control[:refs].uniq! }
|
590
|
+
|
591
|
+
@info_from_parse[:controls] = filter_controls_by_id_and_tags(@info_from_parse[:controls])
|
592
|
+
|
593
|
+
# Update groups after filtering controls to handle --controls option
|
594
|
+
update_groups_from(control_filename, src)
|
595
|
+
|
596
|
+
# NOTE: This is a hack to duplicate inputs.
|
597
|
+
# TODO: Fix this in the input collector or the way we traverse the AST
|
598
|
+
@info_from_parse[:inputs] = @info_from_parse[:inputs].uniq
|
599
|
+
end
|
600
|
+
@info_from_parse
|
601
|
+
end
|
602
|
+
|
603
|
+
def filter_controls_by_id_and_tags(controls)
|
604
|
+
controls.select do |control|
|
605
|
+
tag_ids = get_all_tags_list(control[:tags])
|
606
|
+
(include_controls_list.empty? || include_controls_list.any? { |control_id| control_id.match?(control[:id]) }) &&
|
607
|
+
(include_tags_list.empty? || include_tags_list.any? { |tag_id| tag_ids.any? { |tag| tag_id.match?(tag) } })
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def get_all_tags_list(control_tags)
|
612
|
+
all_tags = []
|
613
|
+
control_tags.each do |tags|
|
614
|
+
all_tags.push(tags)
|
615
|
+
end
|
616
|
+
all_tags.flatten.compact.uniq.map(&:to_s)
|
617
|
+
rescue
|
618
|
+
[]
|
619
|
+
end
|
620
|
+
|
621
|
+
def include_group_data?(group_data)
|
622
|
+
unless include_controls_list.empty?
|
623
|
+
# {:id=>"controls/example-tmp.rb", :title=>"/ profile", :controls=>["tmp-1.0"]}
|
624
|
+
# Check if the group should be included based on the controls it contains
|
625
|
+
group_data[:controls].any? do |control_id|
|
626
|
+
include_controls_list.any? { |id| id.match?(control_id) }
|
627
|
+
end
|
628
|
+
else
|
629
|
+
true
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
def update_groups_from(control_filename, src)
|
634
|
+
group_data = {
|
635
|
+
id: control_filename,
|
636
|
+
title: nil,
|
637
|
+
}
|
638
|
+
source_location_ref = @source_reader.target.abs_path(control_filename)
|
639
|
+
Inspec::Profile::AstHelper::TitleCollector.new(group_data)
|
640
|
+
.process(src.ast.child_nodes.first) # Picking the title defined for the whole controls file
|
641
|
+
group_controls = @info_from_parse[:controls].select { |control| control[:source_location][:ref] == source_location_ref }
|
642
|
+
group_data[:controls] = group_controls.map { |control| control[:id] }
|
643
|
+
|
644
|
+
@info_from_parse[:groups].push(group_data) if include_group_data?(group_data)
|
645
|
+
end
|
646
|
+
|
517
647
|
def cookstyle_linting_check
|
518
648
|
msgs = []
|
519
649
|
return msgs if Inspec.locally_windows? # See #5723
|
@@ -553,6 +683,122 @@ module Inspec
|
|
553
683
|
end
|
554
684
|
end
|
555
685
|
|
686
|
+
def legacy_check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
687
|
+
# initial values for response object
|
688
|
+
result = {
|
689
|
+
summary: {
|
690
|
+
valid: false,
|
691
|
+
timestamp: Time.now.iso8601,
|
692
|
+
location: @target,
|
693
|
+
profile: nil,
|
694
|
+
controls: 0,
|
695
|
+
},
|
696
|
+
errors: [],
|
697
|
+
warnings: [],
|
698
|
+
offenses: [],
|
699
|
+
}
|
700
|
+
|
701
|
+
entry = lambda { |file, line, column, control, msg|
|
702
|
+
{
|
703
|
+
file: file,
|
704
|
+
line: line,
|
705
|
+
column: column,
|
706
|
+
control_id: control,
|
707
|
+
msg: msg,
|
708
|
+
}
|
709
|
+
}
|
710
|
+
|
711
|
+
warn = lambda { |file, line, column, control, msg|
|
712
|
+
@logger.warn(msg)
|
713
|
+
result[:warnings].push(entry.call(file, line, column, control, msg))
|
714
|
+
}
|
715
|
+
|
716
|
+
error = lambda { |file, line, column, control, msg|
|
717
|
+
@logger.error(msg)
|
718
|
+
result[:errors].push(entry.call(file, line, column, control, msg))
|
719
|
+
}
|
720
|
+
|
721
|
+
offense = lambda { |file, line, column, control, msg|
|
722
|
+
result[:offenses].push(entry.call(file, line, column, control, msg))
|
723
|
+
}
|
724
|
+
|
725
|
+
@logger.info "Checking profile in #{@target}"
|
726
|
+
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
727
|
+
|
728
|
+
# verify metadata
|
729
|
+
m_errors, m_warnings = metadata.valid
|
730
|
+
m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
|
731
|
+
m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
|
732
|
+
m_unsupported = metadata.unsupported
|
733
|
+
m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
|
734
|
+
@logger.info "Metadata OK." if m_errors.empty? && m_unsupported.empty?
|
735
|
+
|
736
|
+
# only run the vendor check if the legacy profile-path is not used as argument
|
737
|
+
if @legacy_profile_path == false
|
738
|
+
# verify that a lockfile is present if we have dependencies
|
739
|
+
unless metadata.dependencies.empty?
|
740
|
+
error.call(meta_path, 0, 0, nil, "Your profile needs to be vendored with `inspec vendor`.") unless lockfile_exists?
|
741
|
+
end
|
742
|
+
|
743
|
+
if lockfile_exists?
|
744
|
+
# verify if metadata and lockfile are out of sync
|
745
|
+
if lockfile.deps.size != metadata.dependencies.size
|
746
|
+
error.call(meta_path, 0, 0, nil, "inspec.yml and inspec.lock are out-of-sync. Please re-vendor with `inspec vendor`.")
|
747
|
+
end
|
748
|
+
|
749
|
+
# verify if metadata and lockfile have the same dependency names
|
750
|
+
metadata.dependencies.each do |dep|
|
751
|
+
# Skip if the dependency does not specify a name
|
752
|
+
next if dep[:name].nil?
|
753
|
+
|
754
|
+
# TODO: should we also verify that the soure is the same?
|
755
|
+
unless lockfile.deps.map { |x| x[:name] }.include? dep[:name]
|
756
|
+
error.call(meta_path, 0, 0, nil, "Cannot find #{dep[:name]} in lockfile. Please re-vendor with `inspec vendor`.")
|
757
|
+
end
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
# extract profile name
|
763
|
+
result[:summary][:profile] = metadata.params[:name]
|
764
|
+
|
765
|
+
count = params[:controls].values.length
|
766
|
+
result[:summary][:controls] = count
|
767
|
+
if count == 0
|
768
|
+
warn.call(nil, nil, nil, nil, "No controls or tests were defined.")
|
769
|
+
else
|
770
|
+
@logger.info("Found #{count} controls.")
|
771
|
+
end
|
772
|
+
|
773
|
+
# iterate over hash of groups
|
774
|
+
params[:controls].each do |id, control|
|
775
|
+
sfile = control[:source_location][:ref]
|
776
|
+
sline = control[:source_location][:line]
|
777
|
+
error.call(sfile, sline, nil, id, "Avoid controls with empty IDs") if id.nil? || id.empty?
|
778
|
+
next if id.start_with? "(generated "
|
779
|
+
|
780
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
|
781
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no descriptions") if control[:descriptions][:default].to_s.empty?
|
782
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
|
783
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
|
784
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? || control[:checks].empty?
|
785
|
+
end
|
786
|
+
|
787
|
+
# Running cookstyle to check for code offenses
|
788
|
+
if @check_cookstyle
|
789
|
+
cookstyle_linting_check.each do |lint_output|
|
790
|
+
data = lint_output.split(":")
|
791
|
+
msg = "#{data[-2]}:#{data[-1]}"
|
792
|
+
offense.call(data[0], data[1], data[2], nil, msg)
|
793
|
+
end
|
794
|
+
end
|
795
|
+
# profile is valid if we could not find any error & offenses
|
796
|
+
result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
|
797
|
+
|
798
|
+
@logger.info "Control definitions OK." if result[:warnings].empty?
|
799
|
+
result
|
800
|
+
end
|
801
|
+
|
556
802
|
# Check if the profile is internally well-structured. The logger will be
|
557
803
|
# used to print information on errors and warnings which are found.
|
558
804
|
#
|
@@ -572,6 +818,9 @@ module Inspec
|
|
572
818
|
offenses: [],
|
573
819
|
}
|
574
820
|
|
821
|
+
# memoize `info_from_parse` with tests
|
822
|
+
info_from_parse(include_tests: true)
|
823
|
+
|
575
824
|
entry = lambda { |file, line, column, control, msg|
|
576
825
|
{
|
577
826
|
file: file,
|
@@ -600,7 +849,7 @@ module Inspec
|
|
600
849
|
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
601
850
|
|
602
851
|
# verify metadata
|
603
|
-
m_errors, m_warnings =
|
852
|
+
m_errors, m_warnings = validity_check
|
604
853
|
m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
|
605
854
|
m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
|
606
855
|
m_unsupported = metadata.unsupported
|
@@ -634,9 +883,9 @@ module Inspec
|
|
634
883
|
end
|
635
884
|
|
636
885
|
# extract profile name
|
637
|
-
result[:summary][:profile] =
|
886
|
+
result[:summary][:profile] = info_from_parse[:name]
|
638
887
|
|
639
|
-
count =
|
888
|
+
count = info_from_parse[:controls].count
|
640
889
|
result[:summary][:controls] = count
|
641
890
|
if count == 0
|
642
891
|
warn.call(nil, nil, nil, nil, "No controls or tests were defined.")
|
@@ -645,9 +894,10 @@ module Inspec
|
|
645
894
|
end
|
646
895
|
|
647
896
|
# iterate over hash of groups
|
648
|
-
|
897
|
+
info_from_parse[:controls].each do |control|
|
649
898
|
sfile = control[:source_location][:ref]
|
650
899
|
sline = control[:source_location][:line]
|
900
|
+
id = control[:id]
|
651
901
|
error.call(sfile, sline, nil, id, "Avoid controls with empty IDs") if id.nil? || id.empty?
|
652
902
|
next if id.start_with? "(generated "
|
653
903
|
|
@@ -673,8 +923,74 @@ module Inspec
|
|
673
923
|
result
|
674
924
|
end
|
675
925
|
|
676
|
-
def
|
677
|
-
|
926
|
+
def validity_check # rubocop:disable Metrics/AbcSize
|
927
|
+
errors = []
|
928
|
+
warnings = []
|
929
|
+
info_from_parse.merge!(metadata.params)
|
930
|
+
|
931
|
+
%w{name version}.each do |field|
|
932
|
+
next unless info_from_parse[field.to_sym].nil?
|
933
|
+
|
934
|
+
errors.push("Missing profile #{field} in #{metadata.ref}")
|
935
|
+
end
|
936
|
+
|
937
|
+
if %r{[\/\\]} =~ info_from_parse[:name]
|
938
|
+
errors.push("The profile name (#{info_from_parse[:name]}) contains a slash" \
|
939
|
+
" which is not permitted. Please remove all slashes from `inspec.yml`.")
|
940
|
+
end
|
941
|
+
|
942
|
+
# if version is set, ensure it is correct
|
943
|
+
if !info_from_parse[:version].nil? && !metadata.valid_version?(info_from_parse[:version])
|
944
|
+
errors.push("Version needs to be in SemVer format")
|
945
|
+
end
|
946
|
+
|
947
|
+
if info_from_parse[:entitlement_id] && info_from_parse[:entitlement_id].strip.empty?
|
948
|
+
errors.push("Entitlement ID should not be blank.")
|
949
|
+
end
|
950
|
+
|
951
|
+
unless metadata.supports_runtime?
|
952
|
+
warnings.push("The current inspec version #{Inspec::VERSION} cannot satisfy profile inspec_version constraint #{info_from_parse[:inspec_version]}")
|
953
|
+
end
|
954
|
+
|
955
|
+
%w{title summary maintainer copyright license}.each do |field|
|
956
|
+
next unless info_from_parse[field.to_sym].nil?
|
957
|
+
|
958
|
+
warnings.push("Missing profile #{field} in #{metadata.ref}")
|
959
|
+
end
|
960
|
+
|
961
|
+
# if license is set, ensure it is in SPDX format or marked as proprietary
|
962
|
+
if !info_from_parse[:license].nil? && !metadata.valid_license?(info_from_parse[:license])
|
963
|
+
warnings.push("License '#{info_from_parse[:license]}' needs to be in SPDX format or marked as 'Proprietary'. See https://spdx.org/licenses/.")
|
964
|
+
end
|
965
|
+
|
966
|
+
# If gem_dependencies is set, it must be an array of hashes with keys name and optional version
|
967
|
+
unless info_from_parse[:gem_dependencies].nil?
|
968
|
+
list = info_from_parse[:gem_dependencies]
|
969
|
+
if list.is_a?(Array) && list.all? { |e| e.is_a? Hash }
|
970
|
+
list.each do |entry|
|
971
|
+
errors.push("gem_dependencies entries must all have a 'name' field") unless entry.key?(:name)
|
972
|
+
if entry[:version]
|
973
|
+
orig = entry[:version]
|
974
|
+
begin
|
975
|
+
# Split on commas as we may have a complex dep
|
976
|
+
orig.split(",").map { |c| Gem::Requirement.parse(c) }
|
977
|
+
rescue Gem::Requirement::BadRequirementError
|
978
|
+
errors.push "Unparseable gem dependency '#{orig}' for #{entry[:name]}"
|
979
|
+
rescue Inspec::GemDependencyInstallError => e
|
980
|
+
errors.push e.message
|
981
|
+
end
|
982
|
+
end
|
983
|
+
extra = (entry.keys - %i{name version})
|
984
|
+
unless extra.empty?
|
985
|
+
warnings.push "Unknown gem_dependencies key(s) #{extra.join(",")} seen for entry '#{entry[:name]}'"
|
986
|
+
end
|
987
|
+
end
|
988
|
+
else
|
989
|
+
errors.push("gem_dependencies must be a List of Hashes")
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
[errors, warnings]
|
678
994
|
end
|
679
995
|
|
680
996
|
def set_status_message(msg)
|
@@ -682,7 +998,6 @@ module Inspec
|
|
682
998
|
end
|
683
999
|
|
684
1000
|
# generates a archive of a folder profile
|
685
|
-
# assumes that the profile was checked before
|
686
1001
|
def archive(opts)
|
687
1002
|
# check if file exists otherwise overwrite the archive
|
688
1003
|
dst = archive_name(opts)
|
@@ -699,31 +1014,36 @@ module Inspec
|
|
699
1014
|
# TODO ignore all .files, but add the files to debug output
|
700
1015
|
|
701
1016
|
# Generate temporary inspec.json for archive
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
1017
|
+
export_opt_enabled = opts[:export] || opts[:legacy_export]
|
1018
|
+
if export_opt_enabled
|
1019
|
+
info_for_profile_summary = opts[:legacy_export] ? info : info_from_parse
|
1020
|
+
Inspec::Utils::JsonProfileSummary.produce_json(
|
1021
|
+
info: info_for_profile_summary,
|
1022
|
+
write_path: "#{root_path}inspec.json",
|
1023
|
+
suppress_output: true
|
1024
|
+
)
|
1025
|
+
end
|
707
1026
|
|
708
1027
|
# display all files that will be part of the archive
|
709
1028
|
@logger.debug "Add the following files to archive:"
|
710
1029
|
files.each { |f| @logger.debug " " + f }
|
711
|
-
@logger.debug " inspec.json"
|
1030
|
+
@logger.debug " inspec.json" if export_opt_enabled
|
712
1031
|
|
1032
|
+
archive_files = export_opt_enabled ? files.push("inspec.json") : files
|
713
1033
|
if opts[:zip]
|
714
1034
|
# generate zip archive
|
715
1035
|
require "inspec/archive/zip"
|
716
1036
|
zag = Inspec::Archive::ZipArchiveGenerator.new
|
717
|
-
zag.archive(root_path,
|
1037
|
+
zag.archive(root_path, archive_files, dst)
|
718
1038
|
else
|
719
1039
|
# generate tar archive
|
720
1040
|
require "inspec/archive/tar"
|
721
1041
|
tag = Inspec::Archive::TarArchiveGenerator.new
|
722
|
-
tag.archive(root_path,
|
1042
|
+
tag.archive(root_path, archive_files, dst)
|
723
1043
|
end
|
724
1044
|
|
725
1045
|
# Cleanup
|
726
|
-
FileUtils.rm_f("#{root_path}inspec.json")
|
1046
|
+
FileUtils.rm_f("#{root_path}inspec.json") if export_opt_enabled
|
727
1047
|
|
728
1048
|
@logger.info "Finished archive generation."
|
729
1049
|
true
|
@@ -829,10 +1149,12 @@ module Inspec
|
|
829
1149
|
return Pathname.new(name)
|
830
1150
|
end
|
831
1151
|
|
832
|
-
|
1152
|
+
# Using metadata to fetch basic info of name and version
|
1153
|
+
metadata = @source_reader.metadata.params
|
1154
|
+
name = metadata[:name] ||
|
833
1155
|
raise("Cannot create an archive without a profile name! Please "\
|
834
1156
|
"specify the name in metadata or use --output to create the archive.")
|
835
|
-
version =
|
1157
|
+
version = metadata[:version] ||
|
836
1158
|
raise("Cannot create an archive without a profile version! Please "\
|
837
1159
|
"specify the version in metadata or use --output to create the archive.")
|
838
1160
|
ext = opts[:zip] ? "zip" : "tar.gz"
|
@@ -319,15 +319,9 @@ module Inspec::Resources
|
|
319
319
|
return nil
|
320
320
|
end
|
321
321
|
|
322
|
-
resolve_ipv4 = resolve_ipv4.inject(:merge) if resolve_ipv4.is_a?(Array)
|
323
|
-
|
324
322
|
# Append the ipv4 addresses
|
325
|
-
resolve_ipv4
|
326
|
-
|
327
|
-
next if matched.nil? || addresses.include?(matched.to_s)
|
328
|
-
|
329
|
-
addresses << matched.to_s
|
330
|
-
end
|
323
|
+
resolve_ipv4 = [resolve_ipv4] unless resolve_ipv4.is_a?(Array)
|
324
|
+
resolve_ipv4.each { |entry| addresses << entry["IPAddress"] }
|
331
325
|
|
332
326
|
# -Type AAAA is the DNS query for IPv6 server Address.
|
333
327
|
cmd = inspec.command("Resolve-DnsName –Type AAAA #{hostname} | ConvertTo-Json")
|
@@ -337,15 +331,9 @@ module Inspec::Resources
|
|
337
331
|
return nil
|
338
332
|
end
|
339
333
|
|
340
|
-
resolve_ipv6 = resolve_ipv6.inject(:merge) if resolve_ipv6.is_a?(Array)
|
341
|
-
|
342
334
|
# Append the ipv6 addresses
|
343
|
-
resolve_ipv6
|
344
|
-
|
345
|
-
next if matched.nil? || addresses.include?(matched.to_s)
|
346
|
-
|
347
|
-
addresses << matched.to_s
|
348
|
-
end
|
335
|
+
resolve_ipv6 = [resolve_ipv6] unless resolve_ipv6.is_a?(Array)
|
336
|
+
resolve_ipv6.each { |entry| addresses << entry["IPAddress"] }
|
349
337
|
|
350
338
|
addresses
|
351
339
|
end
|
@@ -169,9 +169,14 @@ module Inspec::Resources
|
|
169
169
|
# special handling for string values with "
|
170
170
|
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
171
171
|
m[1]
|
172
|
+
# We get some values of Registry Path as MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel=4,0
|
173
|
+
# which we are not going to split as there are chances that it will break if anyone is using string comparison.
|
174
|
+
# In some cases privilege value which does not have corresponding SID it returns the values in comma seprated which breakes it for some of
|
175
|
+
# the privileges like SeServiceLogonRight as it returns array if previlege values are SID
|
176
|
+
elsif !key.include?("\\") && val.match(/,/)
|
177
|
+
val.split(",")
|
172
178
|
else
|
173
|
-
|
174
|
-
key.include?("\\") ? val : val.split(",")
|
179
|
+
val
|
175
180
|
end
|
176
181
|
end
|
177
182
|
|
data/lib/inspec/rule.rb
CHANGED
@@ -63,6 +63,11 @@ module Inspec
|
|
63
63
|
# Rubocop thinks we are raising an exception - we're actually calling RSpec's fail()
|
64
64
|
its(location) { fail e.message } # rubocop: disable Style/SignalException
|
65
65
|
end
|
66
|
+
|
67
|
+
# instance_eval evaluates the describe block and raise errors if at the resource level any execution is failed
|
68
|
+
# Waived controls expect not to raise any controls and get skipped if run is false so __apply_waivers needs to be called here too
|
69
|
+
# so that waived control are actually gets waived.
|
70
|
+
__apply_waivers
|
66
71
|
end
|
67
72
|
end
|
68
73
|
|