inspec-core 5.22.3 → 5.22.36
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/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
|
|