compliance_engine 0.2.2 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fce58aedae595351e68af63ff5af15c884bb0b8449c3ec4e2ebb71ebbad873d9
4
- data.tar.gz: cf5c54d0cde492c74df83bfd7701779c8db1a79a289ca34825f868faacffab03
3
+ metadata.gz: f5129c30257114df1399ae314ff6a0fb5d383de5bd4782b8b31dc060ea274870
4
+ data.tar.gz: 8dfaa1d2feaf1a43ea6551851eac5a586f2d4e2b4148efcd14cad21c32c93cba
5
5
  SHA512:
6
- metadata.gz: 35511b511226cdd62a750d3d39e8f40cc9edece3fc72cc310356b05da62679b6ca241ee4d60f88eef731e4ce252627436288414b95f32f3125ce2918f9d9a61b
7
- data.tar.gz: fef7c63498f348f9d92d2c61e35d00e59822a59cfad779244d948b10c744a026eb304a23ff40da90d26142a710a775f0370697755e790563f6ba8736cf218ec3
6
+ metadata.gz: b4839e2648adfc6c1ea91d16c45a5494a6ad4da6521d816e16b9d0b3127b80d696d6f0cbb5249294cf6e0f21177809fbe9b8efd6c3665e3aae1bee6a02570134
7
+ data.tar.gz: 68a0a04f93e3c007eec4287a92d6d19ace0ba37e9a739200474802ef28c6cb24b01257cec5e23f8b8e2cb8f29cf397641ac1e2fc5a1c117ca1db43f1932c2c3d
data/AGENTS.md ADDED
@@ -0,0 +1,134 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to AI agents when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ This is a Ruby gem (`compliance_engine`) that parses and works with [Sicura/SIMP Compliance Engine (SCE)](https://simp-project.com/docs/sce/) data. It also ships as a Puppet module providing a Hiera backend (`compliance_engine::enforcement`) for enforcing compliance profiles in Puppet environments.
8
+
9
+ ## Commands
10
+
11
+ ### Testing
12
+ ```bash
13
+ # Run all tests and rubocop (default task)
14
+ bundle exec rake
15
+
16
+ # Run just spec tests (with fixture prep/cleanup)
17
+ bundle exec rake spec
18
+
19
+ # Run spec tests standalone (no fixture prep)
20
+ bundle exec rake spec:standalone
21
+
22
+ # Run rubocop linting
23
+ bundle exec rake rubocop
24
+
25
+ # Run a single spec file
26
+ bundle exec rspec spec/classes/compliance_engine/data_spec.rb
27
+
28
+ # Run tests in parallel (used in CI for Ruby < 4.0)
29
+ bundle exec rake parallel_spec
30
+ ```
31
+
32
+ ### Development
33
+ ```bash
34
+ # Install dependencies
35
+ bundle install
36
+
37
+ # Open interactive shell with compliance data loaded
38
+ bundle exec compliance_engine inspect --module /path/to/module
39
+
40
+ # CLI usage examples
41
+ bundle exec compliance_engine profiles --modulepath /path/to/modules
42
+ bundle exec compliance_engine hiera --profile my_profile --modulepath /path/to/modules
43
+ bundle exec compliance_engine lookup some::class::param --profile my_profile --module /path/to/module
44
+ ```
45
+
46
+ ## Architecture
47
+
48
+ ### Data Model
49
+
50
+ Compliance data lives in YAML/JSON files at `<module>/SIMP/compliance_profiles/*.yaml` or `<module>/simp/compliance_profiles/*.yaml`. Files are structured with four top-level keys: `profiles`, `ce` (Compliance Elements), `checks`, and `controls`.
51
+
52
+ The library models this data with a two-layer class hierarchy:
53
+
54
+ **Collections** (`ComplianceEngine::Collection` subclass) hold named groups of components:
55
+ - `ComplianceEngine::Profiles` — keyed by `'profiles'` in source data
56
+ - `ComplianceEngine::Ces` — keyed by `'ce'` in source data
57
+ - `ComplianceEngine::Checks` — keyed by `'checks'` in source data
58
+ - `ComplianceEngine::Controls` — keyed by `'controls'` in source data
59
+
60
+ **Components** (`ComplianceEngine::Component` subclass) represent individual named entries within those collections:
61
+ - `ComplianceEngine::Profile` — a named compliance profile
62
+ - `ComplianceEngine::Ce` — a Compliance Element (CE)
63
+ - `ComplianceEngine::Check` — a single compliance check; only `type: puppet-class-parameter` checks produce Hiera data via `Check#hiera`
64
+ - `ComplianceEngine::Control` — a compliance control
65
+
66
+ A component can have multiple **fragments** (one per source file), which are deep-merged together via `deep_merge`. Confinement logic in `Component` filters fragments based on Puppet facts, module presence/version, and remediation risk level.
67
+
68
+ ### Central Data Object
69
+
70
+ `ComplianceEngine::Data` is the primary entry point. It:
71
+ 1. Loads files via `open(*paths)` which delegates to `ModuleLoader` → `DataLoader::Yaml/Json`
72
+ 2. Uses Ruby's `Observable` pattern — `DataLoader` objects notify `Data` of changes
73
+ 3. Lazily constructs and caches the four collection objects; invalidates all caches when facts, enforcement_tolerance, modulepath, or environment_data change
74
+ 4. Exposes `Data#hiera(profiles)` which walks the check_mapping of requested profiles to produce a flat Hiera-compatible hash
75
+
76
+ ### Business Logic: From Profiles to Hiera
77
+
78
+ **`Data#hiera(profile_names)`** is the primary output method. It:
79
+ 1. Resolves each name to a `Profile` object (logs and skips unknown names).
80
+ 2. Calls `Data#check_mapping(profile)` for each profile to find all associated checks.
81
+ 3. Filters to checks with `type: 'puppet-class-parameter'`.
82
+ 4. Calls `Check#hiera` on each, which returns `{ settings['parameter'] => settings['value'] }`.
83
+ 5. Deep-merges all results into a single flat hash and caches it.
84
+
85
+ **`Data#check_mapping(profile_or_ce)`** is the correlation engine that links profiles (or CEs) to checks. A check is included if **any** of the following hold (evaluated via `Data#mapping?`):
86
+
87
+ | Condition | What it checks |
88
+ |-----------|---------------|
89
+ | Shared **control** | `check.controls` and `profile.controls` share a key set to `true` |
90
+ | Shared **CE** | `check.ces` and `profile.ces` share a key set to `true` |
91
+ | CE→Control overlap | Any of `check.ces`' CEs has a control that also appears in `profile.controls` |
92
+ | Direct reference | `profile.checks[check_key]` is truthy |
93
+
94
+ `check_mapping` can also be called with CE objects (in addition to profiles). Results are cached by `"#{object.class}:#{object.key}"`.
95
+
96
+ ### Loading Pipeline
97
+
98
+ ```
99
+ paths → EnvironmentLoader → ModuleLoader (one per module dir)
100
+ → DataLoader::Yaml / DataLoader::Json
101
+ ↓ (Observable notify)
102
+ ComplianceEngine::Data#update
103
+ ```
104
+
105
+ - `EnvironmentLoader` scans a Puppet modulepath for module directories
106
+ - `EnvironmentLoader::Zip` handles zip-archived environments
107
+ - `ModuleLoader` reads a module's `metadata.json` and discovers compliance data files
108
+ - `DataLoader` (and its subclasses) read and parse individual files; they use the Observable pattern to push updates to `Data`
109
+
110
+ ### Puppet Hiera Backend
111
+
112
+ `lib/puppet/functions/compliance_engine/enforcement.rb` implements the Hiera `lookup_key` function. It:
113
+ - Resolves profiles from `compliance_engine::enforcement` and optionally `compliance_markup::enforcement` Hiera keys
114
+ - Creates and caches a `ComplianceEngine::Data` object on the Puppet lookup context
115
+ - Calls `data.hiera(profiles)` and bulk-caches results for subsequent lookups
116
+ - Supports `compliance_markup` backwards compatibility via `compliance_markup_compatibility` option
117
+
118
+ ### Confinement and Enforcement Tolerance
119
+
120
+ `Component#fragments` filters source fragments based on:
121
+ - **Fact confinement** (`confine` key): dot-notation Puppet facts (e.g. `os.release.major`). Values may be a string (exact match), a string prefixed with `!` (negation), or an array (any match). Implemented in `Component#fact_match?`. Fact confinement is skipped when `facts` is `nil`.
122
+ - **Module confinement** (`confine.module_name` + `confine.module_version`): checks against `environment_data` (a `{module_name => version}` hash) using semantic versioning. Module confinement only runs when `environment_data` is set (e.g. by `ComplianceEngine::Data#open`).
123
+ - **Remediation risk** (`remediation.risk`): when `enforcement_tolerance` is a positive `Integer`, drops fragments where risk level ≥ `enforcement_tolerance` and drops disabled remediations. Only applies to `Check` components.
124
+
125
+ In practice, only fact confinement is bypassed when `facts` is `nil`; module confinement still applies whenever `environment_data` is available. All confinement and risk/disabled-remediation filtering are effectively bypassed only when both `facts` and `environment_data` are unset and `enforcement_tolerance` is not a positive `Integer` (every fragment is then included). This is useful for offline analysis where system context and enforcement settings are unavailable.
126
+
127
+ ### Code Style
128
+
129
+ Rubocop is configured via `.rubocop.yml` inheriting from `voxpupuli-test`. Key style choices:
130
+ - `compact` class/module nesting style (e.g. `class ComplianceEngine::Data` not nested modules)
131
+ - Trailing commas on multiline args/arrays
132
+ - Leading dot position for method chaining
133
+ - `braces_for_chaining` block delimiters
134
+ - Max line length: 200
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ### 0.4.0 / 2026-04-20
2
+ * Route Puppet Hiera backend log messages through Puppet's logging system (#96)
3
+ * Fix JRuby compatibility: convert internal requires to require_relative so the library loads correctly inside Puppet Server / OpenVox Server when installed as a Puppet module rather than a standalone gem
4
+ * Replace PDK with puppet-modulebuilder for Puppet Forge builds
5
+ * Add Beaker acceptance test suite covering puppet apply, server/agent, cache leakage, and environment leakage scenarios
6
+
7
+ ### 0.3.0 / 2026-03-19
8
+ * Hash-like Collection methods return Collection objects (#37)
9
+
1
10
  ### 0.2.2 / 2026-03-02
2
11
  * Ensure that cloned/duped objects get independent collection instances
3
12
 
data/README.md CHANGED
@@ -42,6 +42,54 @@ Options:
42
42
 
43
43
  See the [`ComplianceEngine::Data`](https://rubydoc.info/gems/compliance_engine/ComplianceEngine/Data) class for details.
44
44
 
45
+ ## Concepts
46
+
47
+ ### Data Model
48
+
49
+ Compliance data is expressed across four entity types that live in YAML/JSON files inside Puppet modules (`<module>/SIMP/compliance_profiles/*.yaml`):
50
+
51
+ | Entity | Key | Purpose |
52
+ |--------|-----|---------|
53
+ | **Profile** | `profiles` | A named compliance standard (e.g. `nist_800_53_rev4`). References CEs, checks, and/or controls that together constitute that standard. |
54
+ | **CE** (Compliance Element) | `ce` | A single, named compliance capability (e.g. "enable audit logging"). Bridges profiles to checks via a shared vocabulary. |
55
+ | **Check** | `checks` | A verifiable assertion about a system setting. Checks of `type: puppet-class-parameter` carry a `parameter` and `value` that become Hiera data. |
56
+ | **Control** | `controls` | A cross-reference label from an external framework (e.g. `nist_800_53:rev4:AU-2`). Profiles and checks both annotate themselves with controls to express alignment. |
57
+
58
+ ### From Profiles to Hiera Data
59
+
60
+ The central operation of the library is `Data#hiera(profiles)`, which converts a list of profile names into a flat hash of Puppet class parameters and their enforced values:
61
+
62
+ ```
63
+ profile names
64
+ ↓ check_mapping: find all checks that belong to each profile
65
+ checks (type: puppet-class-parameter only)
66
+ ↓ Check#hiera: extract { 'class::param' => value }
67
+ deep-merged hash → { 'widget_spinner::audit_logging' => true, ... }
68
+ ```
69
+
70
+ **How check_mapping works** — a check is considered part of a profile if any of the following are true:
71
+
72
+ 1. The check and profile share a **control** label (`nist_800_53:rev4:AU-2`).
73
+ 2. The check and profile share a **CE** reference.
74
+ 3. The check's CE and the profile share a **control** label.
75
+ 4. The profile explicitly lists the check by key under its `checks:` map.
76
+
77
+ This layered matching lets compliance authors express mappings at different levels of abstraction and have the engine resolve them automatically.
78
+
79
+ ### Confinement
80
+
81
+ A component (profile, CE, check, or control) may be defined across multiple source files. Each file contributes a **fragment**. Before fragments are merged, they are filtered by:
82
+
83
+ - **Facts** (`confine:` key): dot-notation Puppet facts, optionally negated with a `!` prefix. A fragment is dropped if its confinement does not match the current system's facts.
84
+ - **Module presence/version** (`confine.module_name` / `confine.module_version`): fragment is dropped if the required module is absent or the wrong version.
85
+ - **Remediation risk/status** (`remediation.risk` / `remediation.disabled`): when `enforcement_tolerance` is set to a positive Integer, a fragment is dropped if remediation is explicitly `disabled` or if its risk level is ≥ `enforcement_tolerance`.
86
+
87
+ If `facts` is `nil`, all fact/module confinement is skipped; fragments are still subject to remediation-based filtering when `enforcement_tolerance` is set.
88
+
89
+ ### Enforcement Tolerance
90
+
91
+ `enforcement_tolerance` is an optional integer threshold that controls how cautiously the engine applies remediations. When it is set to a positive Integer, fragments whose `remediation.risk.level` meets or exceeds the threshold, or whose remediation is explicitly `disabled`, are silently excluded from the merged result, allowing operators to tune aggressiveness (e.g. apply only low-risk remediations in production, all remediations in a test environment). When `enforcement_tolerance` is `nil` or not a positive Integer, no remediation-based filtering occurs and `remediation.risk` / `remediation.disabled` do not affect fragment inclusion.
92
+
45
93
  ## Using as a Puppet Module
46
94
 
47
95
  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.
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ['lib']
26
26
 
27
27
  spec.add_dependency 'deep_merge', '~> 1.2'
28
- spec.add_dependency 'irb', '~> 1.14'
28
+ spec.add_dependency 'irb', '~> 1.14' unless RUBY_PLATFORM == 'java'
29
29
  spec.add_dependency 'logger', '~> 1.4'
30
30
  spec.add_dependency 'observer', '~> 0.1'
31
31
  spec.add_dependency 'rubyzip', '>= 2.3', '< 4'
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A compliance engine data CE
6
6
  class ComplianceEngine::Ce < ComplianceEngine::Component
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A collection of compliance engine data CEs
6
6
  class ComplianceEngine::Ces < ComplianceEngine::Collection
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A compliance engine data check
6
6
  class ComplianceEngine::Check < ComplianceEngine::Component
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A collection of compliance engine data checks
6
6
  class ComplianceEngine::Checks < ComplianceEngine::Collection
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
  require 'thor'
5
5
 
6
6
  # Compliance Engine CLI
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A generic compliance engine data collection
6
6
  class ComplianceEngine::Collection
@@ -67,6 +67,13 @@ class ComplianceEngine::Collection
67
67
  to_h.keys
68
68
  end
69
69
 
70
+ # Returns the values of the collection
71
+ #
72
+ # @return [Array] the values of the collection
73
+ def values
74
+ to_h.values
75
+ end
76
+
70
77
  # Return a single value from the collection
71
78
  #
72
79
  # @param key [String] the key of the value to return
@@ -78,22 +85,34 @@ class ComplianceEngine::Collection
78
85
  # Iterates over the collection
79
86
  #
80
87
  # @param block [Proc] the block to execute
88
+ # @return [self, Enumerator]
81
89
  def each(&block)
90
+ return to_enum(:each) unless block
91
+
82
92
  to_h.each(&block)
93
+ self
83
94
  end
84
95
 
85
96
  # Iterates over values in the collection
86
97
  #
87
98
  # @param block [Proc] the block to execute
99
+ # @return [self, Enumerator]
88
100
  def each_value(&block)
101
+ return to_enum(:each_value) unless block
102
+
89
103
  to_h.each_value(&block)
104
+ self
90
105
  end
91
106
 
92
107
  # Iterates over keys in the collection
93
108
  #
94
109
  # @param block [Proc] the block to execute
110
+ # @return [self, Enumerator]
95
111
  def each_key(&block)
112
+ return to_enum(:each_key) unless block
113
+
96
114
  to_h.each_key(&block)
115
+ self
97
116
  end
98
117
 
99
118
  # Return true if any of the values in the collection match the block
@@ -115,17 +134,35 @@ class ComplianceEngine::Collection
115
134
  # Select values in the collection
116
135
  #
117
136
  # @param block [Proc] the block to execute
118
- # @return [Hash] the filtered hash
137
+ # @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given
119
138
  def select(&block)
120
- to_h.select(&block)
139
+ return to_enum(:select) unless block_given?
140
+
141
+ result = dup
142
+ result.collection = result.to_h.select(&block)
143
+ result
121
144
  end
122
145
 
146
+ alias filter select
147
+
123
148
  # Filter out values in the collection
124
149
  #
125
150
  # @param block [Proc] the block to execute
126
- # @return [Hash] the filtered hash
151
+ # @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given
127
152
  def reject(&block)
128
- to_h.reject(&block)
153
+ return to_enum(:reject) unless block_given?
154
+
155
+ result = dup
156
+ result.collection = result.to_h.reject(&block)
157
+ result
158
+ end
159
+
160
+ # Transform values in the collection
161
+ #
162
+ # @param block [Proc] the block to execute
163
+ # @return [Hash, Enumerator] a hash with transformed values, or an Enumerator when no block is given
164
+ def transform_values(&block)
165
+ to_h.transform_values(&block)
129
166
  end
130
167
 
131
168
  private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
  require 'deep_merge'
5
5
 
6
6
  # A generic compliance engine data component
@@ -216,14 +216,14 @@ class ComplianceEngine::Component
216
216
 
217
217
  if fragment['remediation'].key?('disabled')
218
218
  message = "Remediation disabled for #{fragment}"
219
- reason = fragment['remediation']['disabled']&.map { |value| value['reason'] }&.reject(&:nil?)&.join("\n")
219
+ reason = fragment['remediation']['disabled']&.map { |value| value['reason'] }&.compact&.join("\n")
220
220
  message += "\n#{reason}" unless reason.nil?
221
221
  ComplianceEngine.log.info message
222
222
  return true
223
223
  end
224
224
 
225
225
  if fragment['remediation'].key?('risk')
226
- risk_level = fragment['remediation']['risk']&.map { |value| value['level'] }&.select { |value| value.is_a?(Integer) }&.max
226
+ risk_level = fragment['remediation']['risk']&.map { |value| value['level'] }&.grep(Integer)&.max
227
227
  if risk_level.is_a?(Integer) && risk_level >= enforcement_tolerance
228
228
  ComplianceEngine.log.info "Remediation risk #{risk_level} exceeds enforcement enforcement_tolerance #{enforcement_tolerance} for #{fragment}"
229
229
  return true
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A compliance engine data control
6
6
  class ComplianceEngine::Control < ComplianceEngine::Component
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A collection of compliance engine data controls
6
6
  class ComplianceEngine::Controls < ComplianceEngine::Collection
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/version'
5
- require 'compliance_engine/component'
6
- require 'compliance_engine/ce'
7
- require 'compliance_engine/check'
8
- require 'compliance_engine/control'
9
- require 'compliance_engine/profile'
10
- require 'compliance_engine/collection'
11
- require 'compliance_engine/ces'
12
- require 'compliance_engine/checks'
13
- require 'compliance_engine/controls'
14
- require 'compliance_engine/profiles'
15
-
16
- require 'compliance_engine/data_loader'
17
- require 'compliance_engine/data_loader/json'
18
- require 'compliance_engine/data_loader/yaml'
19
- require 'compliance_engine/module_loader'
20
- require 'compliance_engine/environment_loader'
3
+ require_relative '../compliance_engine'
4
+ require_relative 'version'
5
+ require_relative 'component'
6
+ require_relative 'ce'
7
+ require_relative 'check'
8
+ require_relative 'control'
9
+ require_relative 'profile'
10
+ require_relative 'collection'
11
+ require_relative 'ces'
12
+ require_relative 'checks'
13
+ require_relative 'controls'
14
+ require_relative 'profiles'
15
+
16
+ require_relative 'data_loader'
17
+ require_relative 'data_loader/json'
18
+ require_relative 'data_loader/yaml'
19
+ require_relative 'module_loader'
20
+ require_relative 'environment_loader'
21
21
 
22
22
  require 'deep_merge'
23
23
  require 'json'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/data_loader'
3
+ require_relative '../../compliance_engine'
4
+ require_relative '../data_loader'
5
5
 
6
6
  # Load compliance engine data from a file
7
7
  class ComplianceEngine::DataLoader::File < ComplianceEngine::DataLoader
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/data_loader/file'
3
+ require_relative '../../compliance_engine'
4
+ require_relative 'file'
5
5
 
6
6
  require 'json'
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/data_loader/file'
3
+ require_relative '../../compliance_engine'
4
+ require_relative 'file'
5
5
 
6
6
  require 'yaml'
7
7
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
  require 'observer'
5
5
 
6
6
  # Load compliance engine data
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/environment_loader'
3
+ require_relative '../../compliance_engine'
4
+ require_relative '../environment_loader'
5
5
  require 'zip/filesystem'
6
6
 
7
7
  # Load compliance engine data from a zip file containing a Puppet environment
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/module_loader'
3
+ require_relative '../compliance_engine'
4
+ require_relative 'module_loader'
5
5
 
6
6
  # Load compliance engine data from a Puppet environment
7
7
  class ComplianceEngine::EnvironmentLoader
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
4
- require 'compliance_engine/data_loader/json'
5
- require 'compliance_engine/data_loader/yaml'
3
+ require_relative '../compliance_engine'
4
+ require_relative 'data_loader/json'
5
+ require_relative 'data_loader/yaml'
6
6
 
7
7
  # Load compliance engine data from a Puppet module
8
8
  class ComplianceEngine::ModuleLoader
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A compliance engine data profile
6
6
  class ComplianceEngine::Profile < ComplianceEngine::Component
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine'
3
+ require_relative '../compliance_engine'
4
4
 
5
5
  # A collection of compliance engine data profiles
6
6
  class ComplianceEngine::Profiles < ComplianceEngine::Collection
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComplianceEngine
4
+ # Routes ComplianceEngine log messages through Puppet's logging system.
5
+ # Used as a drop-in replacement for the default Logger when running inside Puppet.
6
+ class PuppetLogger
7
+ def initialize(*_args, **_kwargs)
8
+ ensure_puppet_available!
9
+ end
10
+
11
+ def debug(msg)
12
+ ensure_puppet_available!
13
+ ::Puppet.debug(msg)
14
+ end
15
+
16
+ def info(msg)
17
+ ensure_puppet_available!
18
+ ::Puppet.info(msg)
19
+ end
20
+
21
+ def warn(msg)
22
+ ensure_puppet_available!
23
+ ::Puppet.warning(msg)
24
+ end
25
+
26
+ def error(msg)
27
+ ensure_puppet_available!
28
+ ::Puppet.err(msg)
29
+ end
30
+
31
+ def fatal(msg)
32
+ ensure_puppet_available!
33
+ ::Puppet.crit(msg)
34
+ end
35
+
36
+ def level; end
37
+
38
+ def level=(_val); end
39
+
40
+ private
41
+
42
+ def ensure_puppet_available!
43
+ return if defined?(::Puppet)
44
+
45
+ raise ComplianceEngine::Error,
46
+ 'ComplianceEngine::PuppetLogger requires Puppet to be loaded, but ::Puppet is not defined'
47
+ end
48
+ end
49
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ComplianceEngine
4
- VERSION = '0.2.2'
4
+ VERSION = '0.4.0'
5
5
 
6
6
  # Handle supported compliance data versions
7
7
  class Version
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'compliance_engine/version'
4
- require 'compliance_engine/data'
3
+ require_relative 'compliance_engine/version'
4
+ require_relative 'compliance_engine/data'
5
5
  require 'logger'
6
6
 
7
7
  # Work with compliance data
@@ -26,7 +26,7 @@ module ComplianceEngine
26
26
 
27
27
  # Get the logger
28
28
  #
29
- # @return [Logger]
29
+ # @return [Logger, ComplianceEngine::PuppetLogger]
30
30
  def self.log
31
31
  return @log unless @log.nil?
32
32
 
@@ -36,8 +36,20 @@ module ComplianceEngine
36
36
  end
37
37
 
38
38
  # Set the logger
39
- # @param logger [Logger] The logger to use
39
+ #
40
+ # @param value [Logger, ComplianceEngine::PuppetLogger] The logger to use
40
41
  def self.log=(value)
41
42
  @log = value
42
43
  end
44
+
45
+ # Install a PuppetLogger unless a logger has already been explicitly configured.
46
+ # Extracted so the behaviour can be unit-tested without reloading enforcement.rb.
47
+ #
48
+ # @return [void]
49
+ def self.install_puppet_logger
50
+ return unless @log.nil?
51
+
52
+ require_relative 'compliance_engine/puppet_logger'
53
+ @log = PuppetLogger.new
54
+ end
43
55
  end
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.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Pritchard
@@ -120,6 +120,7 @@ executables:
120
120
  extensions: []
121
121
  extra_rdoc_files: []
122
122
  files:
123
+ - AGENTS.md
123
124
  - CHANGELOG.md
124
125
  - LICENSE
125
126
  - README.md
@@ -146,6 +147,7 @@ files:
146
147
  - lib/compliance_engine/module_loader.rb
147
148
  - lib/compliance_engine/profile.rb
148
149
  - lib/compliance_engine/profiles.rb
150
+ - lib/compliance_engine/puppet_logger.rb
149
151
  - lib/compliance_engine/version.rb
150
152
  homepage: https://simp-project.com/docs/sce/
151
153
  licenses:
@@ -153,7 +155,7 @@ licenses:
153
155
  metadata:
154
156
  homepage_uri: https://simp-project.com/docs/sce/
155
157
  source_code_uri: https://github.com/simp/rubygem-simp-compliance_engine
156
- changelog_uri: https://github.com/simp/rubygem-simp-compliance_engine/releases/tag/0.2.2
158
+ changelog_uri: https://github.com/simp/rubygem-simp-compliance_engine/releases/tag/0.4.0
157
159
  bug_tracker_uri: https://github.com/simp/rubygem-simp-compliance_engine/issues
158
160
  rdoc_options: []
159
161
  require_paths:
@@ -169,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
171
  - !ruby/object:Gem::Version
170
172
  version: '0'
171
173
  requirements: []
172
- rubygems_version: 4.0.3
174
+ rubygems_version: 4.0.6
173
175
  specification_version: 4
174
176
  summary: Parser for Sicura Compliance Engine data
175
177
  test_files: []