compliance_engine 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +623 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +22 -0
- data/README.md +31 -0
- data/Rakefile +12 -0
- data/TODO.md +19 -0
- data/exe/compliance_engine +6 -0
- data/lib/compliance_engine/ce.rb +7 -0
- data/lib/compliance_engine/ces.rb +40 -0
- data/lib/compliance_engine/check.rb +38 -0
- data/lib/compliance_engine/checks.rb +22 -0
- data/lib/compliance_engine/cli.rb +76 -0
- data/lib/compliance_engine/collection.rb +132 -0
- data/lib/compliance_engine/component.rb +251 -0
- data/lib/compliance_engine/control.rb +7 -0
- data/lib/compliance_engine/controls.rb +22 -0
- data/lib/compliance_engine/data.rb +426 -0
- data/lib/compliance_engine/data_loader/file.rb +35 -0
- data/lib/compliance_engine/data_loader/json.rb +17 -0
- data/lib/compliance_engine/data_loader/yaml.rb +17 -0
- data/lib/compliance_engine/data_loader.rb +45 -0
- data/lib/compliance_engine/environment_loader/zip.rb +24 -0
- data/lib/compliance_engine/environment_loader.rb +31 -0
- data/lib/compliance_engine/module_loader.rb +62 -0
- data/lib/compliance_engine/profile.rb +13 -0
- data/lib/compliance_engine/profiles.rb +22 -0
- data/lib/compliance_engine/version.rb +24 -0
- data/lib/compliance_engine.rb +25 -0
- data/sig/compliance_engine.rbs +4 -0
- metadata +145 -0
data/TODO.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Unimplemented features, in no particular order:
|
2
|
+
|
3
|
+
- [x] Limit ces/checks/controls based on selected profile
|
4
|
+
- [x] Correlation between ces/controls and checks
|
5
|
+
- [ ] Test merge of profiles (ordering of settings)
|
6
|
+
- [ ] Test malformed data
|
7
|
+
- [ ] Storage and resolution of facts
|
8
|
+
- [ ] Confinement
|
9
|
+
- [ ] Enforcement tolerance
|
10
|
+
- [ ] Hiera backend functionality
|
11
|
+
- [ ] Add missing documentation
|
12
|
+
- [ ] Reset state when files are updated
|
13
|
+
- [ ] Shared state between multiple objects (store file data)
|
14
|
+
- [ ] Command-line tools for examining compliance data
|
15
|
+
- [ ] Lint support (replace `scelint`)
|
16
|
+
- [ ] Resolve oval ids to CEs
|
17
|
+
- [ ] Puppet environment support
|
18
|
+
- [ ] Read/store metadata
|
19
|
+
- [ ] Load compliance data from a module path
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
|
5
|
+
# A collection of compliance engine data CEs
|
6
|
+
class ComplianceEngine::Ces < ComplianceEngine::Collection
|
7
|
+
# A Hash of CEs by OVAL ID
|
8
|
+
#
|
9
|
+
# @return [Hash]
|
10
|
+
def by_oval_id
|
11
|
+
return @by_oval_id unless @by_oval_id.nil?
|
12
|
+
|
13
|
+
@by_oval_id ||= {}
|
14
|
+
|
15
|
+
each do |k, v|
|
16
|
+
v.oval_ids&.each do |oval_id|
|
17
|
+
@by_oval_id[oval_id] ||= {}
|
18
|
+
@by_oval_id[oval_id][k] = v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@by_oval_id
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Returns the key of the collection in compliance engine source data
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
def key
|
31
|
+
'ce'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the class to use for the collection
|
35
|
+
#
|
36
|
+
# @return [Class]
|
37
|
+
def collected
|
38
|
+
ComplianceEngine::Ce
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
|
5
|
+
# A compliance engine data check
|
6
|
+
class ComplianceEngine::Check < ComplianceEngine::Component
|
7
|
+
# Returns the settings of the check
|
8
|
+
#
|
9
|
+
# @return [Hash] the settings of the check
|
10
|
+
def settings
|
11
|
+
element['settings']
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the Puppet class parameters of the check
|
15
|
+
#
|
16
|
+
# @return [Hash] the Puppet class parameters of the check
|
17
|
+
def hiera
|
18
|
+
return @hiera unless @hiera.nil?
|
19
|
+
|
20
|
+
return @hiera = nil unless type == 'puppet-class-parameter'
|
21
|
+
|
22
|
+
@hiera = { settings['parameter'] => settings['value'] }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the type of the check
|
26
|
+
#
|
27
|
+
# @return [String] the type of the check
|
28
|
+
def type
|
29
|
+
element['type']
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the remediation data of the check
|
33
|
+
#
|
34
|
+
# @return [Hash] the remediation data of the check
|
35
|
+
def remediation
|
36
|
+
element['remediation']
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
|
5
|
+
# A collection of compliance engine data checks
|
6
|
+
class ComplianceEngine::Checks < ComplianceEngine::Collection
|
7
|
+
private
|
8
|
+
|
9
|
+
# Returns the key of the collection in compliance engine source data
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
def key
|
13
|
+
'checks'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the class to use for the collection
|
17
|
+
#
|
18
|
+
# @return [Class]
|
19
|
+
def collected
|
20
|
+
ComplianceEngine::Check
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
# Compliance Engine CLI
|
7
|
+
class ComplianceEngine::CLI < Thor
|
8
|
+
class_option :facts, type: :string
|
9
|
+
class_option :enforcement_tolerance, type: :numeric
|
10
|
+
class_option :module, type: :array, default: []
|
11
|
+
class_option :modulepath, type: :array
|
12
|
+
class_option :modulezip, type: :string
|
13
|
+
|
14
|
+
desc 'hiera', 'Dump Hiera data'
|
15
|
+
option :profile, type: :array, required: true
|
16
|
+
def hiera
|
17
|
+
require 'yaml'
|
18
|
+
puts data.hiera(options[:profile]).to_yaml
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'lookup KEY', 'Look up a Hiera key'
|
22
|
+
option :profile, type: :array, required: true
|
23
|
+
def lookup(key)
|
24
|
+
require 'yaml'
|
25
|
+
puts data.hiera(options[:profile]).select { |k, _| k == key }.to_yaml
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'dump', 'Dump all compliance data'
|
29
|
+
def dump
|
30
|
+
require 'yaml'
|
31
|
+
data.files.each do |file|
|
32
|
+
puts({ file => data.get(file) }.to_yaml)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'profiles', 'List available profiles'
|
37
|
+
def profiles
|
38
|
+
require 'yaml'
|
39
|
+
puts data.profiles.select { |_, value| value.ces&.count&.positive? || value.controls&.count&.positive? }.keys.to_yaml
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'inspect', 'Start an interactive shell'
|
43
|
+
def inspect
|
44
|
+
# Run the CLI with `data` as the object containing the compliance data.
|
45
|
+
require 'irb'
|
46
|
+
# rubocop:disable Lint/Debugger
|
47
|
+
binding.irb
|
48
|
+
# rubocop:enable Lint/Debugger
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def data
|
54
|
+
return @data unless @data.nil?
|
55
|
+
|
56
|
+
@data = ComplianceEngine::Data.new(facts: facts, enforcement_tolerance: options[:enforcement_tolerance])
|
57
|
+
if options[:modulezip]
|
58
|
+
@data.open_environment_zip(options[:modulezip])
|
59
|
+
elsif options[:modulepath]
|
60
|
+
@data.open_environment(*options[:modulepath])
|
61
|
+
else
|
62
|
+
@data.open(*options[:module])
|
63
|
+
end
|
64
|
+
|
65
|
+
@data
|
66
|
+
end
|
67
|
+
|
68
|
+
def facts
|
69
|
+
return nil unless options[:facts]
|
70
|
+
return @facts unless @facts.nil?
|
71
|
+
|
72
|
+
require 'json'
|
73
|
+
|
74
|
+
@facts = JSON.parse(options[:facts])
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
|
5
|
+
# A generic compliance engine data collection
|
6
|
+
class ComplianceEngine::Collection
|
7
|
+
# A generic compliance engine data collection
|
8
|
+
#
|
9
|
+
# @param data [ComplianceEngine::Data] the data to initialize the object with
|
10
|
+
def initialize(data)
|
11
|
+
context_variables.each { |var| instance_variable_set(var, data.instance_variable_get(var)) }
|
12
|
+
@collection ||= {}
|
13
|
+
hash_key = key
|
14
|
+
data.files.each do |file|
|
15
|
+
data.get(file)[hash_key]&.each do |k, v|
|
16
|
+
@collection[k] ||= collected.new(k, data: self)
|
17
|
+
@collection[k].add(file, v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :collection, :facts, :enforcement_tolerance, :environment_data
|
23
|
+
|
24
|
+
# Invalidate the cache of computed data
|
25
|
+
#
|
26
|
+
# @param data [ComplianceEngine::Data, NilClass] the data to initialize the object with
|
27
|
+
# @return [NilClass]
|
28
|
+
def invalidate_cache(data = nil)
|
29
|
+
context_variables.each { |var| instance_variable_set(var, data&.instance_variable_get(var)) }
|
30
|
+
collection.each_value { |obj| obj.invalidate_cache(data) }
|
31
|
+
(instance_variables - (context_variables + [:@collection])).each { |var| instance_variable_set(var, nil) }
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Converts the object to a hash representation
|
36
|
+
#
|
37
|
+
# @return [Hash] the hash representation of the object
|
38
|
+
def to_h
|
39
|
+
collection.reject { |k, _| k.is_a?(Symbol) }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the keys of the collection
|
43
|
+
#
|
44
|
+
# @return [Array] the keys of the collection
|
45
|
+
def keys
|
46
|
+
to_h.keys
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return a single value from the collection
|
50
|
+
#
|
51
|
+
# @param key [String] the key of the value to return
|
52
|
+
# @return [Object] the value of the key
|
53
|
+
def [](key)
|
54
|
+
collection[key]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Iterates over the collection
|
58
|
+
#
|
59
|
+
# @param block [Proc] the block to execute
|
60
|
+
def each(&block)
|
61
|
+
to_h.each(&block)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Iterates over values in the collection
|
65
|
+
#
|
66
|
+
# @param block [Proc] the block to execute
|
67
|
+
def each_value(&block)
|
68
|
+
to_h.each_value(&block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Iterates over keys in the collection
|
72
|
+
#
|
73
|
+
# @param block [Proc] the block to execute
|
74
|
+
def each_key(&block)
|
75
|
+
to_h.each_key(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return true if any of the values in the collection match the block
|
79
|
+
#
|
80
|
+
# @param block [Proc] the block to execute
|
81
|
+
# @return [TrueClass, FalseClass] true if any of the values in the collection match the block
|
82
|
+
def any?(&block)
|
83
|
+
to_h.any?(&block)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return true if all of the values in the collection match the block
|
87
|
+
#
|
88
|
+
# @param block [Proc] the block to execute
|
89
|
+
# @return [TrueClass, FalseClass] true if all of the values in the collection match the block
|
90
|
+
def all?(&block)
|
91
|
+
to_h.all?(&block)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Select values in the collection
|
95
|
+
#
|
96
|
+
# @param block [Proc] the block to execute
|
97
|
+
# @return [Hash] the filtered hash
|
98
|
+
def select(&block)
|
99
|
+
to_h.select(&block)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Filter out values in the collection
|
103
|
+
#
|
104
|
+
# @param block [Proc] the block to execute
|
105
|
+
# @return [Hash] the filtered hash
|
106
|
+
def reject(&block)
|
107
|
+
to_h.reject(&block)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Get the context variables
|
113
|
+
#
|
114
|
+
# @return [Array<Symbol>]
|
115
|
+
def context_variables
|
116
|
+
[:@enforcement_tolerance, :@environment_data, :@facts]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns the key of the object
|
120
|
+
#
|
121
|
+
# @return [NotImplementedError] This method is not implemented and should be overridden by subclasses.
|
122
|
+
def key
|
123
|
+
raise NotImplementedError
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the class to use for the collection
|
127
|
+
#
|
128
|
+
# @return [NotImplementedError] This method is not implemented and should be overridden by subclasses.
|
129
|
+
def collected
|
130
|
+
raise NotImplementedError
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
require 'deep_merge'
|
5
|
+
|
6
|
+
# A generic compliance engine data component
|
7
|
+
class ComplianceEngine::Component
|
8
|
+
# A generic compliance engine data component
|
9
|
+
#
|
10
|
+
# @param [String] component The component key
|
11
|
+
# @param [ComplianceEngine::Data, ComplianceEngine::Collection, NilClass] data The data to initialize the object with
|
12
|
+
def initialize(name, data: nil)
|
13
|
+
context_variables.each { |var| instance_variable_set(var, data&.instance_variable_get(var)) }
|
14
|
+
@component ||= { key: name, fragments: {} }
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :component, :facts, :enforcement_tolerance, :environment_data
|
18
|
+
|
19
|
+
# Invalidate the cache of computed data
|
20
|
+
#
|
21
|
+
# @param data [ComplianceEngine::Data, ComplianceEngine::Collection, NilClass] the data to initialize the object with
|
22
|
+
# @return [NilClass]
|
23
|
+
def invalidate_cache(data = nil)
|
24
|
+
context_variables.each { |var| instance_variable_set(var, data&.instance_variable_get(var)) }
|
25
|
+
cache_variables.each { |var| instance_variable_set(var, nil) }
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds a value to the fragments array of the component.
|
30
|
+
#
|
31
|
+
# @param value [Object] The value to be added to the fragments array.
|
32
|
+
# @return [Object]
|
33
|
+
def add(filename, value)
|
34
|
+
component[:fragments][filename] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an array of fragments from the component
|
38
|
+
#
|
39
|
+
# @return [Array] an array of fragments
|
40
|
+
def to_a
|
41
|
+
component[:fragments].values
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the merged data from the component
|
45
|
+
#
|
46
|
+
# @return [Hash] merged data
|
47
|
+
def to_h
|
48
|
+
element
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the key of the component
|
52
|
+
#
|
53
|
+
# @return [String] the key of the component
|
54
|
+
def key
|
55
|
+
component[:key]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the title of the component
|
59
|
+
#
|
60
|
+
# @return [String] the title of the component
|
61
|
+
def title
|
62
|
+
element['title']
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the description of the component
|
66
|
+
#
|
67
|
+
# @return [String] the description of the component
|
68
|
+
def description
|
69
|
+
element['description']
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the oval ids of the component
|
73
|
+
#
|
74
|
+
# @return [Array] the oval ids of the component
|
75
|
+
def oval_ids
|
76
|
+
element['oval-ids']
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the controls of the component
|
80
|
+
#
|
81
|
+
# @return [Hash] the controls of the component
|
82
|
+
def controls
|
83
|
+
element['controls']
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the identifiers of the component
|
87
|
+
#
|
88
|
+
# @return [Hash] the identifiers of the component
|
89
|
+
def identifiers
|
90
|
+
element['identifiers']
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the ces of the component
|
94
|
+
#
|
95
|
+
# @return [Array, Hash] the ces of the component
|
96
|
+
# @note This returns an Array for checks and a Hash for other components
|
97
|
+
def ces
|
98
|
+
element['ces']
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return a single key from the component
|
102
|
+
#
|
103
|
+
# @param key [String] the key of the value to return
|
104
|
+
# @return [Object] the value of the key
|
105
|
+
def [](key)
|
106
|
+
element[key]
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Get the context variables
|
112
|
+
#
|
113
|
+
# @return [Array<Symbol>]
|
114
|
+
def context_variables
|
115
|
+
[:@enforcement_tolerance, :@environment_data, :@facts]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get the cache variables
|
119
|
+
#
|
120
|
+
# @return [Array<Symbol>]
|
121
|
+
def cache_variables
|
122
|
+
instance_variables - (context_variables + [:@component])
|
123
|
+
end
|
124
|
+
|
125
|
+
# Compare a fact value against a confine value
|
126
|
+
#
|
127
|
+
# @param [Object] fact The fact value
|
128
|
+
# @param [Object] confine The confine value
|
129
|
+
# @param [Integer] depth The depth of the recursion
|
130
|
+
# @return [TrueClass, FalseClass] true if the fact value matches the confine value
|
131
|
+
def fact_match?(fact, confine, depth = 0)
|
132
|
+
if confine.is_a?(String)
|
133
|
+
return fact != confine.delete_prefix('!') if confine.start_with?('!')
|
134
|
+
|
135
|
+
fact == confine
|
136
|
+
elsif confine.is_a?(Array)
|
137
|
+
if depth == 0
|
138
|
+
confine.any? { |value| fact_match?(fact, value, depth + 1) }
|
139
|
+
else
|
140
|
+
fact == confine
|
141
|
+
end
|
142
|
+
else
|
143
|
+
fact == confine
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check if a fragment is confined
|
148
|
+
#
|
149
|
+
# @param [Hash] fragment The fragment to check
|
150
|
+
# @return [TrueClass, FalseClass] true if the fragment should be dropped
|
151
|
+
def confine_away?(fragment)
|
152
|
+
return false unless fragment.key?('confine')
|
153
|
+
|
154
|
+
fragment['confine'].each do |k, v|
|
155
|
+
if k == 'module_name'
|
156
|
+
unless environment_data.nil?
|
157
|
+
return true unless environment_data.key?(v)
|
158
|
+
module_version = fragment['confine']['module_version']
|
159
|
+
unless module_version.nil?
|
160
|
+
require 'semantic_puppet'
|
161
|
+
begin
|
162
|
+
return true unless SemanticPuppet::VersionRange.parse(module_version).include?(SemanticPuppet::Version.parse(environment_data[v]))
|
163
|
+
rescue => e
|
164
|
+
warn "Failed to compare #{v} #{environment_data[v]} with version confinement #{module_version}: #{e.message}"
|
165
|
+
return true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
elsif k == 'module_version'
|
170
|
+
warn "Missing module name for #{fragment}" unless fragment['confine'].key?('module_name')
|
171
|
+
else
|
172
|
+
# Confinement based on Puppet facts
|
173
|
+
unless facts.nil?
|
174
|
+
fact = facts.dig(*k.split('.'))
|
175
|
+
if fact.nil?
|
176
|
+
return true
|
177
|
+
end
|
178
|
+
unless fact_match?(fact, v)
|
179
|
+
return true
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
false
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns the fragments of the component after confinement
|
189
|
+
#
|
190
|
+
# @return [Hash] the fragments of the component
|
191
|
+
def fragments
|
192
|
+
return @fragments unless @fragments.nil?
|
193
|
+
|
194
|
+
@fragments ||= {}
|
195
|
+
|
196
|
+
component[:fragments].each do |filename, fragment|
|
197
|
+
# If none of the confinable data is present in the object,
|
198
|
+
# ignore confinement data entirely.
|
199
|
+
if facts.nil? && enforcement_tolerance.nil? && environment_data.nil?
|
200
|
+
@fragments[filename] = fragment
|
201
|
+
next
|
202
|
+
end
|
203
|
+
|
204
|
+
# If no confine data is present in the fragment, include it.
|
205
|
+
if !fragment.key?('confine') && !fragment.key?('remediation')
|
206
|
+
@fragments[filename] = fragment
|
207
|
+
next
|
208
|
+
end
|
209
|
+
|
210
|
+
next if confine_away?(fragment)
|
211
|
+
|
212
|
+
# Confinement based on remediation risk
|
213
|
+
if enforcement_tolerance.is_a?(Integer) && is_a?(ComplianceEngine::Check) && fragment.key?('remediation')
|
214
|
+
if fragment['remediation'].key?('disabled')
|
215
|
+
message = "Remediation disabled for #{fragment}"
|
216
|
+
reason = fragment['remediation']['disabled']&.map { |value| value['reason'] }&.reject { |value| value.nil? }&.join("\n")
|
217
|
+
message += "\n#{reason}" unless reason.nil?
|
218
|
+
warn message
|
219
|
+
next
|
220
|
+
end
|
221
|
+
|
222
|
+
if fragment['remediation'].key?('risk')
|
223
|
+
risk_level = fragment['remediation']['risk']&.map { |value| value['level'] }&.select { |value| value.is_a?(Integer) }&.max
|
224
|
+
if risk_level.is_a?(Integer) && risk_level >= enforcement_tolerance
|
225
|
+
warn "Remediation risk #{risk_level} exceeds enforcement tolerance #{enforcement_tolerance} for #{fragment}"
|
226
|
+
next
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
@fragments[filename] = fragment
|
232
|
+
end
|
233
|
+
|
234
|
+
@fragments
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns a merged view of the component fragments
|
238
|
+
#
|
239
|
+
# @return [Object] the element of the component
|
240
|
+
def element
|
241
|
+
return @element unless @element.nil?
|
242
|
+
|
243
|
+
@element = {}
|
244
|
+
|
245
|
+
fragments.each_value do |fragment|
|
246
|
+
@element = @element.deep_merge!(fragment)
|
247
|
+
end
|
248
|
+
|
249
|
+
@element
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'compliance_engine'
|
4
|
+
|
5
|
+
# A collection of compliance engine data controls
|
6
|
+
class ComplianceEngine::Controls < ComplianceEngine::Collection
|
7
|
+
private
|
8
|
+
|
9
|
+
# Returns the key of the collection in compliance engine source data
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
def key
|
13
|
+
'controls'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the class to use for the collection
|
17
|
+
#
|
18
|
+
# @return [Class]
|
19
|
+
def collected
|
20
|
+
ComplianceEngine::Control
|
21
|
+
end
|
22
|
+
end
|