compliance_engine 0.1.6 → 0.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/CHANGELOG.md +3 -0
- data/README.md +28 -0
- data/REFERENCE.md +42 -0
- data/compliance_engine.gemspec +2 -2
- data/lib/compliance_engine/component.rb +34 -27
- data/lib/compliance_engine/data.rb +6 -6
- data/lib/compliance_engine/data_loader.rb +1 -0
- data/lib/compliance_engine/environment_loader.rb +2 -1
- data/lib/compliance_engine/module_loader.rb +2 -2
- data/lib/compliance_engine/version.rb +2 -1
- data/lib/compliance_engine.rb +1 -1
- metadata +5 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6243625cabdaf239f089f05704cf4a7584d4d7631f6f81ff0cab3bbf8cb4cf49
|
|
4
|
+
data.tar.gz: 81d9b5d6a80799384f052e0dab725fcdbb34804691b591cf8101ff08411ae6c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b29bfb3cbb236ca9b384b5ee08db766a438dbc64aece96f1427a86fdb9389306b606fc12fe3f5bcbfa174ebfe810d88b1dd27fb55f77e084521dabb0db6083c1
|
|
7
|
+
data.tar.gz: 8d95140ec88142904d3614239bf2b1100876650a320fd4db38028493fe9a974d12342083e5f5b2f73fb48f79b5622fd13f6fb0b96940452621e4c82e769014f1
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -42,6 +42,34 @@ Options:
|
|
|
42
42
|
|
|
43
43
|
See the [`ComplianceEngine::Data`](https://rubydoc.info/gems/compliance_engine/ComplianceEngine/Data) class for details.
|
|
44
44
|
|
|
45
|
+
## Using as a Puppet Module
|
|
46
|
+
|
|
47
|
+
The Compliance Engine can be used as a Puppet module to provide a Hiera backend for compliance data. This allows you to enforce compliance profiles through Hiera lookups within your Puppet manifests.
|
|
48
|
+
|
|
49
|
+
### Hiera Backend
|
|
50
|
+
|
|
51
|
+
To use the Compliance Engine Hiera backend, configure it in your `hiera.yaml`:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
---
|
|
55
|
+
version: 5
|
|
56
|
+
hierarchy:
|
|
57
|
+
- name: "Compliance Engine"
|
|
58
|
+
lookup_key: compliance_engine::enforcement
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Specify the profile used by setting the `compliance_engine::enforcement` key in your Hiera data.
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
---
|
|
65
|
+
compliance_engine::enforcement:
|
|
66
|
+
- your_profile
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The `compliance_engine::enforcement` function serves as the Hiera entry point and allows you to look up compliance data based on configured profiles.
|
|
70
|
+
|
|
71
|
+
For detailed information about available functions, parameters, and configuration options, see [REFERENCE.md](REFERENCE.md).
|
|
72
|
+
|
|
45
73
|
## Contributing
|
|
46
74
|
|
|
47
75
|
Bug reports and pull requests are welcome on GitHub at https://github.com/simp/rubygem-simp-compliance_engine.
|
data/REFERENCE.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Reference
|
|
2
|
+
|
|
3
|
+
<!-- DO NOT EDIT: This document was generated by Puppet Strings -->
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
### Functions
|
|
8
|
+
|
|
9
|
+
* [`compliance_engine::enforcement`](#compliance_engine--enforcement): Hiera entry point for Compliance Engine
|
|
10
|
+
|
|
11
|
+
## Functions
|
|
12
|
+
|
|
13
|
+
### <a name="compliance_engine--enforcement"></a>`compliance_engine::enforcement`
|
|
14
|
+
|
|
15
|
+
Type: Ruby 4.x API
|
|
16
|
+
|
|
17
|
+
Hiera entry point for Compliance Engine
|
|
18
|
+
|
|
19
|
+
#### `compliance_engine::enforcement(String[1] $key, Hash[String[1], Any] $options, Puppet::LookupContext $context)`
|
|
20
|
+
|
|
21
|
+
The compliance_engine::enforcement function.
|
|
22
|
+
|
|
23
|
+
Returns: `String` The value of the key in the Hiera data
|
|
24
|
+
|
|
25
|
+
##### `key`
|
|
26
|
+
|
|
27
|
+
Data type: `String[1]`
|
|
28
|
+
|
|
29
|
+
String The key to lookup in the Hiera data
|
|
30
|
+
|
|
31
|
+
##### `options`
|
|
32
|
+
|
|
33
|
+
Data type: `Hash[String[1], Any]`
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
##### `context`
|
|
38
|
+
|
|
39
|
+
Data type: `Puppet::LookupContext`
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
data/compliance_engine.gemspec
CHANGED
|
@@ -19,14 +19,14 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.metadata['bug_tracker_uri'] = 'https://github.com/simp/rubygem-simp-compliance_engine/issues'
|
|
20
20
|
|
|
21
21
|
# Specify which files should be added to the gem when it is released.
|
|
22
|
-
spec.files = Dir.glob(['*.gemspec', '*.md', 'LICENSE', 'exe/*', 'lib/**/*.rb'])
|
|
22
|
+
spec.files = Dir.glob(['*.gemspec', '*.md', 'LICENSE', 'exe/*', 'lib/**/*.rb']).reject { |f| f.start_with?('lib/puppet/') }
|
|
23
23
|
spec.bindir = 'exe'
|
|
24
24
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
25
25
|
spec.require_paths = ['lib']
|
|
26
26
|
|
|
27
27
|
spec.add_dependency 'deep_merge', '~> 1.2'
|
|
28
28
|
spec.add_dependency 'irb', '~> 1.14'
|
|
29
|
-
spec.add_dependency 'logger', '
|
|
29
|
+
spec.add_dependency 'logger', '~> 1.4'
|
|
30
30
|
spec.add_dependency 'observer', '~> 0.1'
|
|
31
31
|
spec.add_dependency 'rubyzip', '>= 2.3', '< 4'
|
|
32
32
|
spec.add_dependency 'semantic_puppet', '~> 1.1'
|
|
@@ -134,7 +134,7 @@ class ComplianceEngine::Component
|
|
|
134
134
|
|
|
135
135
|
fact == confine
|
|
136
136
|
elsif confine.is_a?(Array)
|
|
137
|
-
if depth
|
|
137
|
+
if depth.zero?
|
|
138
138
|
confine.any? { |value| fact_match?(fact, value, depth + 1) }
|
|
139
139
|
else
|
|
140
140
|
fact == confine
|
|
@@ -155,12 +155,13 @@ class ComplianceEngine::Component
|
|
|
155
155
|
if k == 'module_name'
|
|
156
156
|
unless environment_data.nil?
|
|
157
157
|
return true unless environment_data.key?(v)
|
|
158
|
+
|
|
158
159
|
module_version = fragment['confine']['module_version']
|
|
159
160
|
unless module_version.nil?
|
|
160
161
|
require 'semantic_puppet'
|
|
161
162
|
begin
|
|
162
163
|
return true unless SemanticPuppet::VersionRange.parse(module_version).include?(SemanticPuppet::Version.parse(environment_data[v]))
|
|
163
|
-
rescue => e
|
|
164
|
+
rescue StandardError => e
|
|
164
165
|
ComplianceEngine.log.error "Failed to compare #{v} #{environment_data[v]} with version confinement #{module_version}: #{e.message}"
|
|
165
166
|
return true
|
|
166
167
|
end
|
|
@@ -172,12 +173,8 @@ class ComplianceEngine::Component
|
|
|
172
173
|
# Confinement based on Puppet facts
|
|
173
174
|
unless facts.nil?
|
|
174
175
|
fact = facts.dig(*k.split('.'))
|
|
175
|
-
if fact.nil?
|
|
176
|
-
|
|
177
|
-
end
|
|
178
|
-
unless fact_match?(fact, v)
|
|
179
|
-
return true
|
|
180
|
-
end
|
|
176
|
+
return true if fact.nil?
|
|
177
|
+
return true unless fact_match?(fact, v)
|
|
181
178
|
end
|
|
182
179
|
end
|
|
183
180
|
end
|
|
@@ -185,6 +182,34 @@ class ComplianceEngine::Component
|
|
|
185
182
|
false
|
|
186
183
|
end
|
|
187
184
|
|
|
185
|
+
# Check if a fragment is has remediation risk too high or if remediation is disabled
|
|
186
|
+
#
|
|
187
|
+
# @param fragment [Hash] The fragment to check
|
|
188
|
+
# @return [TrueClass, FalseClass] true if the fragment should be dropped
|
|
189
|
+
def risk_too_high?(fragment)
|
|
190
|
+
return false unless is_a?(ComplianceEngine::Check)
|
|
191
|
+
return false unless fragment.key?('remediation')
|
|
192
|
+
return false unless enforcement_tolerance.is_a?(Integer) && enforcement_tolerance.positive?
|
|
193
|
+
|
|
194
|
+
if fragment['remediation'].key?('disabled')
|
|
195
|
+
message = "Remediation disabled for #{fragment}"
|
|
196
|
+
reason = fragment['remediation']['disabled']&.map { |value| value['reason'] }&.reject(&:nil?)&.join("\n")
|
|
197
|
+
message += "\n#{reason}" unless reason.nil?
|
|
198
|
+
ComplianceEngine.log.info message
|
|
199
|
+
return true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if fragment['remediation'].key?('risk')
|
|
203
|
+
risk_level = fragment['remediation']['risk']&.map { |value| value['level'] }&.select { |value| value.is_a?(Integer) }&.max
|
|
204
|
+
if risk_level.is_a?(Integer) && risk_level >= enforcement_tolerance
|
|
205
|
+
ComplianceEngine.log.info "Remediation risk #{risk_level} exceeds enforcement enforcement_tolerance #{enforcement_tolerance} for #{fragment}"
|
|
206
|
+
return true
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
false
|
|
211
|
+
end
|
|
212
|
+
|
|
188
213
|
# Returns the fragments of the component after confinement
|
|
189
214
|
#
|
|
190
215
|
# @return [Hash] the fragments of the component
|
|
@@ -208,25 +233,7 @@ class ComplianceEngine::Component
|
|
|
208
233
|
end
|
|
209
234
|
|
|
210
235
|
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
|
-
ComplianceEngine.log.info 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
|
-
ComplianceEngine.log.info "Remediation risk #{risk_level} exceeds enforcement tolerance #{enforcement_tolerance} for #{fragment}"
|
|
226
|
-
next
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
end
|
|
236
|
+
next if risk_too_high?(fragment)
|
|
230
237
|
|
|
231
238
|
@fragments[filename] = fragment
|
|
232
239
|
end
|
|
@@ -28,7 +28,7 @@ class ComplianceEngine::Data
|
|
|
28
28
|
# @param facts [Hash] The facts to use while evaluating the data
|
|
29
29
|
# @param enforcement_tolerance [Integer] The tolerance to use while evaluating the data
|
|
30
30
|
def initialize(*paths, facts: nil, enforcement_tolerance: nil)
|
|
31
|
-
@data
|
|
31
|
+
@data = {}
|
|
32
32
|
@facts = facts
|
|
33
33
|
@enforcement_tolerance = enforcement_tolerance
|
|
34
34
|
open(*paths) unless paths.nil? || paths.empty?
|
|
@@ -201,7 +201,7 @@ class ComplianceEngine::Data
|
|
|
201
201
|
end
|
|
202
202
|
|
|
203
203
|
reset_collection
|
|
204
|
-
rescue => e
|
|
204
|
+
rescue StandardError => e
|
|
205
205
|
ComplianceEngine.log.error e.message
|
|
206
206
|
end
|
|
207
207
|
|
|
@@ -210,6 +210,7 @@ class ComplianceEngine::Data
|
|
|
210
210
|
# @return [Array<String>]
|
|
211
211
|
def files
|
|
212
212
|
return @files unless @files.nil?
|
|
213
|
+
|
|
213
214
|
@files = data.select { |_, file| file.key?(:content) }.keys
|
|
214
215
|
end
|
|
215
216
|
|
|
@@ -219,7 +220,7 @@ class ComplianceEngine::Data
|
|
|
219
220
|
# @return [Hash]
|
|
220
221
|
def get(file)
|
|
221
222
|
data[file][:content]
|
|
222
|
-
rescue
|
|
223
|
+
rescue StandardError
|
|
223
224
|
nil
|
|
224
225
|
end
|
|
225
226
|
|
|
@@ -263,6 +264,7 @@ class ComplianceEngine::Data
|
|
|
263
264
|
collection.each_value do |v|
|
|
264
265
|
v.to_a.each do |component|
|
|
265
266
|
next unless component.key?('confine')
|
|
267
|
+
|
|
266
268
|
@confines = DeepMerge.deep_merge!(component['confine'], @confines)
|
|
267
269
|
end
|
|
268
270
|
end
|
|
@@ -404,9 +406,7 @@ class ComplianceEngine::Data
|
|
|
404
406
|
# @return [TrueClass, FalseClass]
|
|
405
407
|
def correlate(a, b)
|
|
406
408
|
return false if a.nil? || b.nil?
|
|
407
|
-
unless a.is_a?(Array) && b.is_a?(Hash)
|
|
408
|
-
raise ComplianceEngine::Error, "Expected array and hash, got #{a.class} and #{b.class}"
|
|
409
|
-
end
|
|
409
|
+
raise ComplianceEngine::Error, "Expected array and hash, got #{a.class} and #{b.class}" unless a.is_a?(Array) && b.is_a?(Hash)
|
|
410
410
|
return false if a.empty? || b.empty?
|
|
411
411
|
|
|
412
412
|
a.any? { |item| b[item] }
|
|
@@ -13,6 +13,7 @@ class ComplianceEngine::EnvironmentLoader
|
|
|
13
13
|
# @param zipfile_path [String, nil] the path to the zip file if loading from a zip archive
|
|
14
14
|
def initialize(*paths, fileclass: File, dirclass: Dir, zipfile_path: nil)
|
|
15
15
|
raise ArgumentError, 'No paths specified' if paths.empty?
|
|
16
|
+
|
|
16
17
|
@modulepath ||= paths
|
|
17
18
|
@zipfile_path = zipfile_path
|
|
18
19
|
modules = paths.map do |path|
|
|
@@ -21,7 +22,7 @@ class ComplianceEngine::EnvironmentLoader
|
|
|
21
22
|
.select { |child| fileclass.directory?(File.join(path, child)) }
|
|
22
23
|
.map { |child| File.join(path, child) }
|
|
23
24
|
.sort
|
|
24
|
-
rescue
|
|
25
|
+
rescue StandardError
|
|
25
26
|
[]
|
|
26
27
|
end
|
|
27
28
|
modules.flatten!
|
|
@@ -27,7 +27,7 @@ class ComplianceEngine::ModuleLoader
|
|
|
27
27
|
metadata = ComplianceEngine::DataLoader::Json.new(metadata_json, fileclass: fileclass)
|
|
28
28
|
@name = metadata.data['name']
|
|
29
29
|
@version = metadata.data['version']
|
|
30
|
-
rescue => e
|
|
30
|
+
rescue StandardError => e
|
|
31
31
|
ComplianceEngine.log.warn "Could not parse #{metadata_json}: #{e.message}"
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -53,7 +53,7 @@ class ComplianceEngine::ModuleLoader
|
|
|
53
53
|
ComplianceEngine::DataLoader::Yaml.new(file.to_s, fileclass: fileclass, key: key)
|
|
54
54
|
end
|
|
55
55
|
@files << loader
|
|
56
|
-
rescue => e
|
|
56
|
+
rescue StandardError => e
|
|
57
57
|
ComplianceEngine.log.warn "Could not load #{file}: #{e.message}"
|
|
58
58
|
end
|
|
59
59
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ComplianceEngine
|
|
4
|
-
VERSION = '0.
|
|
4
|
+
VERSION = '0.2.0'
|
|
5
5
|
|
|
6
6
|
# Handle supported compliance data versions
|
|
7
7
|
class Version
|
|
@@ -11,6 +11,7 @@ module ComplianceEngine
|
|
|
11
11
|
def initialize(version)
|
|
12
12
|
raise 'Missing version' if version.nil?
|
|
13
13
|
raise "Unsupported version '#{version}'" unless version == '2.0.0'
|
|
14
|
+
|
|
14
15
|
@version = version
|
|
15
16
|
end
|
|
16
17
|
|
data/lib/compliance_engine.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: compliance_engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Pritchard
|
|
@@ -41,22 +41,16 @@ dependencies:
|
|
|
41
41
|
name: logger
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '1.4'
|
|
47
|
-
- - "<"
|
|
48
|
-
- !ruby/object:Gem::Version
|
|
49
|
-
version: '2.0'
|
|
50
47
|
type: :runtime
|
|
51
48
|
prerelease: false
|
|
52
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
53
50
|
requirements:
|
|
54
|
-
- - "
|
|
51
|
+
- - "~>"
|
|
55
52
|
- !ruby/object:Gem::Version
|
|
56
53
|
version: '1.4'
|
|
57
|
-
- - "<"
|
|
58
|
-
- !ruby/object:Gem::Version
|
|
59
|
-
version: '2.0'
|
|
60
54
|
- !ruby/object:Gem::Dependency
|
|
61
55
|
name: observer
|
|
62
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -129,6 +123,7 @@ files:
|
|
|
129
123
|
- CHANGELOG.md
|
|
130
124
|
- LICENSE
|
|
131
125
|
- README.md
|
|
126
|
+
- REFERENCE.md
|
|
132
127
|
- compliance_engine.gemspec
|
|
133
128
|
- exe/compliance_engine
|
|
134
129
|
- lib/compliance_engine.rb
|
|
@@ -158,7 +153,7 @@ licenses:
|
|
|
158
153
|
metadata:
|
|
159
154
|
homepage_uri: https://simp-project.com/docs/sce/
|
|
160
155
|
source_code_uri: https://github.com/simp/rubygem-simp-compliance_engine
|
|
161
|
-
changelog_uri: https://github.com/simp/rubygem-simp-compliance_engine/releases/tag/0.
|
|
156
|
+
changelog_uri: https://github.com/simp/rubygem-simp-compliance_engine/releases/tag/0.2.0
|
|
162
157
|
bug_tracker_uri: https://github.com/simp/rubygem-simp-compliance_engine/issues
|
|
163
158
|
rdoc_options: []
|
|
164
159
|
require_paths:
|