cem_spec_helper 0.1.1 → 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/lib/cem_spec_helper/mapping_data_spec.rb +88 -4
- data/lib/cem_spec_helper/resource_data_spec.rb +122 -14
- data/lib/cem_spec_helper/version.rb +1 -1
- data/lib/cem_spec_helper.rb +54 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c25d50ec3d0761a950dfd6ddfa13e2b0fa23bf560719ae876af59a6936763a0d
|
4
|
+
data.tar.gz: cca59a9b38dd4d71159ed5d2470c7bac58ba22b1bc31e7b5401e56f74a4124d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37c4136b44f120e95966409d55821c4bc7ab19af6b89951579fbac086b1882cacd36af971af24e9df2697b5222a92395fce90e1d16e9bfa3a2558aa2250bc2b6
|
7
|
+
data.tar.gz: db3690625195985e32e4585511cecd646127878cefd85ee7a9d4d12ed4b9422c49f5c7494f0404e2d758949e0693d5404540c3c84a5e01036addc9f325843e17
|
@@ -4,10 +4,30 @@ require 'yaml'
|
|
4
4
|
|
5
5
|
module CemSpecHelper
|
6
6
|
module MappingDataSpec
|
7
|
+
# The root directory for mapping data fixtures
|
7
8
|
MAP_ROOT = File.join(Dir.pwd, 'spec', 'fixtures', 'data', 'mapping').freeze
|
9
|
+
# The root directory for synthetic mapping data
|
8
10
|
SYNTHETIC_MAP_ROOT = File.join(Dir.pwd, 'spec', 'fixtures', 'unit', 'puppet_x', 'puppetlabs', 'cem', 'data_processor', 'mapping').freeze
|
9
|
-
|
11
|
+
# The key prefix for the mappings
|
12
|
+
MAPPINGS_KEY = "#{CemSpecHelper::MODULE_NAME}::mappings"
|
13
|
+
# The top key types for each framework.
|
14
|
+
TOP_KEY_TYPES = {
|
15
|
+
'cis' => ['hiera_title', 'hiera_title_num', 'number', 'title'].freeze,
|
16
|
+
'stig' => ['vulnid', 'ruleid'].freeze,
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
# Because of the slight variation in the directory structure for Windows, we need to
|
20
|
+
# load the mapping data differently for Windows than for Linux.
|
21
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
10
22
|
def load_mapping_data
|
23
|
+
return load_windows_mapping_data if CemSpecHelper::MODULE_NAME.match?(/windows$/)
|
24
|
+
|
25
|
+
load_linux_mapping_data
|
26
|
+
end
|
27
|
+
|
28
|
+
# Load the mapping data for Linux
|
29
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
30
|
+
def load_linux_mapping_data
|
11
31
|
# Get frameworks with maps
|
12
32
|
Dir[File.join(MAP_ROOT, '*')].each_with_object([]) do |fw_dir, arr|
|
13
33
|
framework = File.basename(fw_dir)
|
@@ -29,6 +49,28 @@ module CemSpecHelper
|
|
29
49
|
end
|
30
50
|
end
|
31
51
|
|
52
|
+
# Load the mapping data for Windows
|
53
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
54
|
+
def load_windows_mapping_data
|
55
|
+
# Get frameworks with maps
|
56
|
+
Dir[File.join(MAP_ROOT, '*')].each_with_object([]) do |fw_dir, arr|
|
57
|
+
framework = File.basename(fw_dir)
|
58
|
+
# Get OS major versions in frameworks
|
59
|
+
Dir[File.join(fw_dir, '*')].each do |ver_dir|
|
60
|
+
ver = File.basename(ver_dir)
|
61
|
+
mapping = Mapping.new(framework, 'windows', ver)
|
62
|
+
# Get map names and load map files
|
63
|
+
Dir[File.join(ver_dir, '*.yaml')].each do |map_file|
|
64
|
+
map_type = File.basename(map_file, '.yaml')
|
65
|
+
mapping.add_map(map_type, map_file)
|
66
|
+
end
|
67
|
+
arr << mapping
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Load the synthetic mapping data
|
73
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
32
74
|
def load_synthetic_mapping_data
|
33
75
|
# Get frameworks with maps
|
34
76
|
Dir[File.join(SYNTHETIC_MAP_ROOT, '*')].each_with_object([]) do |fw_dir, arr|
|
@@ -43,37 +85,74 @@ module CemSpecHelper
|
|
43
85
|
end
|
44
86
|
end
|
45
87
|
|
88
|
+
# Find the mapping data for a given framework, OS, and major OS version, and return the maps
|
89
|
+
# filtered by level and profile.
|
90
|
+
# @param framework [String] The framework to find the mapping data for
|
91
|
+
# @param os [String] The OS to find the mapping data for
|
92
|
+
# @param majver [String] The major OS version to find the mapping data for
|
93
|
+
# @param level [String] The level to filter the maps by. For STIG, this is the MAC.
|
94
|
+
# @param profile [String] The profile to filter the maps by. For STIG, this is the confidentiality.
|
95
|
+
# @return [Array<Hash>] An array of hashes representing the maps
|
96
|
+
def find_mapping_data(framework, os, majver = nil, level = 'level_1', profile = 'server')
|
97
|
+
majver = 1 if os == 'Synthetic'
|
98
|
+
md = (os == 'Synthetic') ? synthetic_mapping_data : mapping_data
|
99
|
+
md.find { |m| m.framework == framework && m.os == os && m.majver == majver.to_s }.maps
|
100
|
+
.map do |m|
|
101
|
+
top_key = TOP_KEY_TYPES[framework]&.find do |key|
|
102
|
+
m.key?("#{MAPPINGS_KEY}::#{framework}::#{key}")
|
103
|
+
end
|
104
|
+
raise "No top key found for #{framework}" if top_key.nil?
|
105
|
+
|
106
|
+
m["#{MAPPINGS_KEY}::#{framework}::#{top_key}"][level][profile]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
46
111
|
def synthetic_mapping_data
|
47
112
|
@synthetic_mapping_data ||= load_synthetic_mapping_data
|
48
113
|
end
|
49
114
|
|
115
|
+
# @return [Array<Mapping>] An array of Mapping objects
|
50
116
|
def mapping_data
|
51
117
|
@mapping_data ||= load_mapping_data
|
52
118
|
end
|
53
119
|
|
120
|
+
# This class represents a mapping of a framework, OS, and major OS version.
|
54
121
|
class Mapping
|
55
122
|
attr_reader :framework, :os, :majver, :module_name
|
56
123
|
|
57
|
-
|
124
|
+
# @param framework [String] The framework to create the mapping for
|
125
|
+
# @param os [String] The OS to create the mapping for
|
126
|
+
# @param majver [String] The major OS version to create the mapping for
|
127
|
+
def initialize(framework, os, majver)
|
58
128
|
@framework = framework
|
59
129
|
@os = os
|
60
130
|
@majver = majver
|
61
|
-
@module_name =
|
131
|
+
@module_name = CemSpecHelper::MODULE_NAME
|
62
132
|
@maps = {}
|
63
133
|
end
|
64
134
|
|
135
|
+
# @return [Array<Hash>] An array of hashes representing the maps
|
65
136
|
def maps
|
66
137
|
@maps.values
|
67
138
|
end
|
68
139
|
|
140
|
+
# Add a map to the mapping
|
141
|
+
# @param type [String] The type of map to add
|
142
|
+
# @param map_file [String] The path to the map file to add
|
69
143
|
def add_map(type, map_file)
|
70
144
|
@maps[type] = YAML.load_file(map_file)
|
71
145
|
end
|
72
146
|
|
147
|
+
# @return [Array<String>] An array of the map types
|
73
148
|
def map_types
|
74
149
|
@maps.keys
|
75
150
|
end
|
76
151
|
|
152
|
+
# Verify that all the mappings have the same size
|
153
|
+
# @param keys_with_array_val [Hash] A hash of keys with array values
|
154
|
+
# @return [TrueClass] True if all the mappings have the same size
|
155
|
+
# @raise [RuntimeError] If the mappings do not have the same size
|
77
156
|
def verify_map_size(keys_with_array_val = find_keys_with_array_val(@maps.dup))
|
78
157
|
result = keys_with_array_val.each_with_object([]) do |(id, map), res|
|
79
158
|
actual_length = 1 + map.length
|
@@ -84,6 +163,10 @@ module CemSpecHelper
|
|
84
163
|
true
|
85
164
|
end
|
86
165
|
|
166
|
+
# Verify that all mappings are one-to-one, or that there are no duplicate IDs shared between
|
167
|
+
# maps.
|
168
|
+
# @return [TrueClass] True if all mappings are one-to-one
|
169
|
+
# @raise [RuntimeError] If there are duplicate IDs shared between maps
|
87
170
|
def verify_one_to_one
|
88
171
|
results = @maps.keys.each_with_object([]) do |t, arr|
|
89
172
|
keys_with_array_val = find_keys_with_array_val(@maps[t])
|
@@ -103,6 +186,7 @@ module CemSpecHelper
|
|
103
186
|
true
|
104
187
|
end
|
105
188
|
|
189
|
+
# @return [String] A string representation of the mapping
|
106
190
|
def to_s
|
107
191
|
"Mapping[#{framework}, #{os}, #{majver}]"
|
108
192
|
end
|
@@ -110,7 +194,7 @@ module CemSpecHelper
|
|
110
194
|
private
|
111
195
|
|
112
196
|
def top_key(type)
|
113
|
-
[
|
197
|
+
[MAPPINGS_KEY, framework, type].join('::')
|
114
198
|
end
|
115
199
|
|
116
200
|
def find_keys_with_array_val(hsh)
|
@@ -4,25 +4,46 @@ require 'yaml'
|
|
4
4
|
|
5
5
|
module CemSpecHelper
|
6
6
|
module ResourceDataSpec
|
7
|
+
# The root directory for all resource data fixtures
|
7
8
|
DATA_ROOT = File.join(Dir.pwd, 'spec', 'fixtures', 'data')
|
9
|
+
# The root directory for RedHat family resource data fixtures
|
8
10
|
REDHAT_FAMILY_ROOT = File.join(DATA_ROOT, 'RedHat')
|
11
|
+
# The root directory for RedHat resource data fixtures
|
9
12
|
REDHAT_ROOT_DIR = File.join(REDHAT_FAMILY_ROOT, 'RedHat')
|
13
|
+
# The major versions of RedHat resource data fixtures
|
10
14
|
REDHAT_MAJVER = [7, 8].freeze
|
15
|
+
# The root directory for CentOS resource data fixtures
|
11
16
|
CENTOS_ROOT_DIR = File.join(REDHAT_FAMILY_ROOT, 'CentOS')
|
17
|
+
# The major versions of CentOS resource data fixtures
|
12
18
|
CENTOS_MAJVER = [7].freeze
|
19
|
+
# The root directory for OracleLinux resource data fixtures
|
13
20
|
ORACLE_ROOT_DIR = File.join(REDHAT_FAMILY_ROOT, 'OracleLinux')
|
21
|
+
# The major versions of OracleLinux resource data fixtures
|
14
22
|
ORACLE_MAJVER = [7, 8].freeze
|
23
|
+
# The root directory for AlmaLinux resource data fixtures
|
15
24
|
ALMA_ROOT_DIR = File.join(REDHAT_FAMILY_ROOT, 'AlmaLinux')
|
25
|
+
# The major versions of AlmaLinux resource data fixtures
|
16
26
|
ALMA_MAJVER = [8].freeze
|
27
|
+
# The root directory for Windows resource data fixtures
|
17
28
|
WINDOWS_ROOT_DIR = File.join(DATA_ROOT, 'windows', 'windows')
|
29
|
+
# The major versions of Windows resource data fixtures
|
18
30
|
WINDOWS_MAJVER = [10, 2016, 2019, 2022].freeze
|
19
|
-
|
31
|
+
# The root directory for synthetic resource data fixtures
|
20
32
|
SYNTHETIC_DATA_ROOT = File.join(Dir.pwd, 'spec', 'fixtures', 'unit', 'puppet_x', 'puppetlabs', 'cem', 'data_processor')
|
21
|
-
|
33
|
+
# The special controls that are not mapped to a framework
|
22
34
|
SPECIAL_CONTROLS = ['cem_options', 'cem_protected'].freeze
|
35
|
+
# The key prefix for the resources
|
36
|
+
RESOURCES_KEY = "#{CemSpecHelper::MODULE_NAME}::resources"
|
23
37
|
|
24
|
-
|
25
|
-
|
38
|
+
# Finds and loads resource data for a given OS and major version
|
39
|
+
# @param osname [String] The name of the OS
|
40
|
+
# @param majver [String] The major version of the OS
|
41
|
+
# @param as_objects [Boolean] Whether or not to return the resource data as objects or hashes
|
42
|
+
# @return [Array<Hash>] An array of hashes containing the resource data
|
43
|
+
# @return [Array<Resource>] An array of Resource objects
|
44
|
+
# @raise [ArgumentError] If the OS name or major version is not found
|
45
|
+
def find_resource_data(osname, majver = nil, as_objects: false)
|
46
|
+
case osname
|
26
47
|
when 'RedHat'
|
27
48
|
redhat_resource_data(majver, as_objects: as_objects)
|
28
49
|
when 'CentOS'
|
@@ -31,45 +52,66 @@ module CemSpecHelper
|
|
31
52
|
oracle_resource_data(majver, as_objects: as_objects)
|
32
53
|
when 'AlmaLinux'
|
33
54
|
alma_resource_data(majver, as_objects: as_objects)
|
34
|
-
when
|
55
|
+
when /^[Ww]indows$/
|
35
56
|
windows_resource_data(majver, as_objects: as_objects)
|
36
|
-
when
|
37
|
-
|
57
|
+
when /^[Ss]ynthetic/
|
58
|
+
synthetic_resource_data(as_objects: as_objects)
|
38
59
|
else
|
39
|
-
raise "Unknown
|
60
|
+
raise "Unknown OS: #{osname}"
|
40
61
|
end
|
41
62
|
end
|
42
63
|
|
64
|
+
# Shortcut methods for loading resource data for a specific OS
|
65
|
+
# @param majver [String] The major version of the OS
|
66
|
+
# @param as_objects [Boolean] Whether or not to return the resource data as objects or hashes
|
67
|
+
# @return [Array<Hash>] An array of hashes containing the resource data
|
68
|
+
# @return [Array<Resource>] An array of Resource objects
|
69
|
+
# @raise [ArgumentError] If the major version is not found
|
43
70
|
def redhat_resource_data(majver = nil, as_objects: false)
|
44
71
|
raise ArgumentError, "major version #{majver} not found" unless majver.nil? || REDHAT_MAJVER.include?(majver.to_i)
|
45
72
|
|
46
73
|
load_resource_data(REDHAT_ROOT_DIR, majver, as_objects: as_objects)
|
47
74
|
end
|
48
75
|
|
76
|
+
# @param (see #redhat_resource_data)
|
77
|
+
# @return (see #redhat_resource_data)
|
78
|
+
# @raise (see #redhat_resource_data)
|
49
79
|
def centos_resource_data(majver = nil, as_objects: false)
|
50
80
|
raise ArgumentError, "major version #{majver} not found" unless majver.nil? || CENTOS_MAJVER.include?(majver.to_i)
|
51
81
|
|
52
82
|
load_resource_data(CENTOS_ROOT_DIR, majver, as_objects: as_objects)
|
53
83
|
end
|
54
84
|
|
85
|
+
# @param (see #redhat_resource_data)
|
86
|
+
# @return (see #redhat_resource_data)
|
87
|
+
# @raise (see #redhat_resource_data)
|
55
88
|
def oracle_resource_data(majver = nil, as_objects: false)
|
56
89
|
raise ArgumentError, "major version #{majver} not found" unless majver.nil? || ORACLE_MAJVER.include?(majver.to_i)
|
57
90
|
|
58
91
|
load_resource_data(ORACLE_ROOT_DIR, majver, as_objects: as_objects)
|
59
92
|
end
|
60
93
|
|
94
|
+
# @param (see #redhat_resource_data)
|
95
|
+
# @return (see #redhat_resource_data)
|
96
|
+
# @raise (see #redhat_resource_data)
|
61
97
|
def alma_resource_data(majver = nil, as_objects: false)
|
62
98
|
raise ArgumentError, "major version #{majver} not found" unless majver.nil? || ALMA_MAJVER.include?(majver.to_i)
|
63
99
|
|
64
100
|
load_resource_data(ALMA_ROOT_DIR, majver, as_objects: as_objects)
|
65
101
|
end
|
66
102
|
|
103
|
+
# @param (see #redhat_resource_data)
|
104
|
+
# @return (see #redhat_resource_data)
|
105
|
+
# @raise (see #redhat_resource_data)
|
67
106
|
def windows_resource_data(majver = nil, as_objects: false)
|
68
107
|
raise ArgumentError, "major version #{majver} not found" unless majver.nil? || WINDOWS_MAJVER.include?(majver.to_i)
|
69
108
|
|
70
109
|
load_resource_data(WINDOWS_ROOT_DIR, majver, as_objects: as_objects)
|
71
110
|
end
|
72
111
|
|
112
|
+
# Shortcut method for loading synthetic resource data
|
113
|
+
# @param as_objects [Boolean] Whether or not to return the resource data as objects or hashes
|
114
|
+
# @return (see #redhat_resource_data)
|
73
115
|
def synthetic_resource_data(as_objects: false)
|
74
116
|
load_resource_data(SYNTHETIC_DATA_ROOT, 'test_resource_data', as_objects: as_objects)
|
75
117
|
end
|
@@ -78,10 +120,12 @@ module CemSpecHelper
|
|
78
120
|
# @param distro [String]
|
79
121
|
# @param majver [String]
|
80
122
|
# @param benchmark [String] either cis or stig
|
123
|
+
# @param max [nil, Integer] maximum number of resources to return. If nil, returns all
|
81
124
|
# @return [Hash] A hash of control_name => [resource type, resource title]
|
82
|
-
def single_control_resources(distro, majver = nil, benchmark = 'cis')
|
125
|
+
def single_control_resources(distro, majver = nil, benchmark = 'cis', max: nil)
|
83
126
|
resources = find_resource_data(distro, majver, as_objects: true)
|
84
127
|
resources.select! { |res| res.controls(include_special: false).length == 1 }
|
128
|
+
resources = max.nil? ? resources : resources.first(max)
|
85
129
|
resources.map! do |res|
|
86
130
|
[res.controls.first, [res.type, res.title]]
|
87
131
|
end
|
@@ -96,34 +140,94 @@ module CemSpecHelper
|
|
96
140
|
resources
|
97
141
|
end
|
98
142
|
|
143
|
+
# Finds all resources that implement multiple controls
|
144
|
+
# @param distro [String]
|
145
|
+
# @param majver [String]
|
146
|
+
# @param benchmark [String] either cis or stig
|
147
|
+
# @param max [nil, Integer] maximum number of resources to return
|
148
|
+
# @return [Hash] A hash of control_name => [resource type, resource title]
|
149
|
+
def multi_control_resources(distro, majver = nil, benchmark = 'cis', max: nil)
|
150
|
+
resources = find_resource_data(distro, majver, as_objects: true)
|
151
|
+
resources.select! { |res| res.controls(include_special: false).length > 1 }
|
152
|
+
resources = max.nil? ? resources : resources.first(max)
|
153
|
+
resources.map! do |res|
|
154
|
+
[res.controls, [res.type, res.title]]
|
155
|
+
end
|
156
|
+
resources = resources.to_h
|
157
|
+
# Reduce to just benchmark-related keypairs
|
158
|
+
case benchmark
|
159
|
+
when 'cis'
|
160
|
+
resources.reject! { |k, _v| k.match?(%r{^V-}) }
|
161
|
+
when 'stig'
|
162
|
+
resources.select! { |k, _v| k.match?(%r{^V-}) }
|
163
|
+
end
|
164
|
+
resources
|
165
|
+
end
|
166
|
+
|
167
|
+
# Loads resource data from a given root directory and major version
|
168
|
+
# @param root_dir [String] The root directory to load resource data from
|
169
|
+
# @param majver [Integer, String, nil] The major version of the OS. If nil,
|
170
|
+
# loads all resource data from the root directory.
|
171
|
+
# @param as_objects [Boolean] Whether or not to return the resource data as objects or hashes
|
172
|
+
# @return [Array<Hash>] An array of hashes containing the resource data
|
173
|
+
# @return [Array<Resource>] An array of Resource objects
|
174
|
+
# @return [Hash] A hash of found major versions => resource data arrays. Is only returned
|
175
|
+
# if the majver parameter is nil.
|
176
|
+
# @raise [ArgumentError] If the root directory is not a valid path
|
177
|
+
# @raise [RuntimeError] If the resource data file is not found
|
99
178
|
def load_resource_data(root_dir, majver = nil, as_objects: false)
|
100
|
-
raise "root_dir \"#{root_dir}\" is not a valid path" unless File.directory?(root_dir)
|
179
|
+
raise ArgumentError, "root_dir \"#{root_dir}\" is not a valid path" unless File.directory?(root_dir)
|
101
180
|
|
102
181
|
unless majver.nil?
|
103
182
|
file_path = File.join(root_dir, "#{majver}.yaml")
|
104
183
|
raise "Resource data file \"#{file_path}\" not found" unless File.file?(file_path)
|
105
184
|
|
106
|
-
resources = YAML.load_file(file_path)[
|
185
|
+
resources = YAML.load_file(file_path)[RESOURCES_KEY]
|
107
186
|
final_resources = as_objects ? resources.map { |k, v| Resource.new(k, v) } : resources
|
108
187
|
return final_resources
|
109
188
|
end
|
110
189
|
|
111
190
|
Dir[File.join(root_dir, '*')].each_with_object({}) do |rdata, hsh|
|
112
|
-
resources = YAML.load_file(rdata)[
|
191
|
+
resources = YAML.load_file(rdata)[RESOURCES_KEY]
|
113
192
|
final_resources = as_objects ? resources.map { |k, v| Resource.new(k, v) } : resources
|
114
193
|
hsh[File.basename(rdata, '.yaml')] = final_resources
|
115
194
|
end
|
116
195
|
end
|
117
196
|
|
197
|
+
# Finds all controls in the given array of Resource objects
|
198
|
+
# @param rdata_objects [Array<Resource>] An array of Resource objects
|
199
|
+
# @return [Array<String>] An array of control names
|
200
|
+
def all_controls(rdata_objects)
|
201
|
+
rdata_objects.map(&:controls).flatten
|
202
|
+
end
|
203
|
+
|
204
|
+
# Finds all controls that are duplicated in the given array of Resource objects
|
205
|
+
# @param rdata_objects [Array<Resource>] An array of Resource objects
|
206
|
+
# @return [Array<String>] An array of control names
|
118
207
|
def duplicate_controls(rdata_objects)
|
119
|
-
|
208
|
+
all = all_controls(rdata_objects)
|
120
209
|
# Lets go O(n^2) solution!
|
121
|
-
|
210
|
+
all.select { |c| all.count(c) > 1 }.reject { |c| SPECIAL_CONTROLS.include?(c) }.uniq
|
211
|
+
end
|
212
|
+
|
213
|
+
# Finds all controls that are not mapped in the given array of Resource objects and mapping data array
|
214
|
+
# @param rdata_objects [Array<Resource>] An array of Resource objects
|
215
|
+
# @param mdata_array [Array<Hash>] An array of hashes containing mapping data
|
216
|
+
# @return [Array<String>] An array of control names
|
217
|
+
def not_in_mapping_data(rdata_objects, mdata_array)
|
218
|
+
all = all_controls(rdata_objects)
|
219
|
+
mdata_array.compact.each do |m|
|
220
|
+
all -= m.keys
|
221
|
+
end
|
222
|
+
all.reject { |c| SPECIAL_CONTROLS.include?(c) }.uniq
|
122
223
|
end
|
123
224
|
|
225
|
+
# The Resource class represents a single resource from the resource data file
|
124
226
|
class Resource
|
125
227
|
attr_reader :title, :type
|
126
228
|
|
229
|
+
# @param title [String] The title of the resource
|
230
|
+
# @param data [Hash] The resource data
|
127
231
|
def initialize(title, data)
|
128
232
|
@title = title
|
129
233
|
@data = data
|
@@ -132,6 +236,8 @@ module CemSpecHelper
|
|
132
236
|
@control_data = load_control_data
|
133
237
|
end
|
134
238
|
|
239
|
+
# @return [Array<String>] An array of control names implemented by the resource
|
240
|
+
# @param include_special [Boolean] Whether or not to include special controls (cem_options, cem_protected)
|
135
241
|
def controls(include_special: true)
|
136
242
|
ctrls = @no_params ? @control_data : @control_data.keys
|
137
243
|
return ctrls if include_special
|
@@ -139,12 +245,14 @@ module CemSpecHelper
|
|
139
245
|
ctrls.reject { |c| SPECIAL_CONTROLS.include?(c) }
|
140
246
|
end
|
141
247
|
|
248
|
+
# @return [Hash] A hash of parameter names => default values
|
142
249
|
def params
|
143
250
|
# rubocop:disable Style/CollectionMethods
|
144
251
|
control_data.map { |_, v| v if v.is_a?(Hash) }.flatten.compact.uniq.inject(:merge)
|
145
252
|
# rubocop:enable Style/CollectionMethods
|
146
253
|
end
|
147
254
|
|
255
|
+
# @return [TrueClass, FalseClass] Whether or not the resource has any duplicate control names
|
148
256
|
def verify_no_duplicate_controls
|
149
257
|
controls.uniq == controls
|
150
258
|
end
|
data/lib/cem_spec_helper.rb
CHANGED
@@ -1,20 +1,71 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This module is meant to be included in the spec_helper.rb file.
|
4
|
+
# It is not meant to be used directly.
|
3
5
|
module CemSpecHelper
|
6
|
+
MODULE_NAME = File.basename(Dir.pwd).gsub(/^puppetlabs-/, '')
|
7
|
+
|
4
8
|
# Makes it so the CemSpecHelper module has to be included in the spec_helper.rb file.
|
5
9
|
def self.included(base)
|
6
10
|
require 'cem_spec_helper/mapping_data_spec'
|
7
11
|
require 'cem_spec_helper/resource_data_spec'
|
8
12
|
require 'cem_spec_helper/version'
|
9
13
|
|
10
|
-
module PuppetX; end
|
11
|
-
module PuppetX::CEM; end
|
12
|
-
|
13
14
|
RSpec.configure do |config|
|
15
|
+
config.include CemSpecHelper::GeneralSpec
|
16
|
+
config.extend CemSpecHelper::GeneralSpec
|
14
17
|
config.include CemSpecHelper::MappingDataSpec
|
15
18
|
config.extend CemSpecHelper::MappingDataSpec
|
16
19
|
config.include CemSpecHelper::ResourceDataSpec
|
17
20
|
config.extend CemSpecHelper::ResourceDataSpec
|
18
21
|
end
|
19
22
|
end
|
23
|
+
|
24
|
+
module GeneralSpec
|
25
|
+
# The type of control ID used in the mapping data, resource data, or control configs.
|
26
|
+
# @param id [String] The control ID
|
27
|
+
# @param framework [String] The framework to use. If nil, will try both CIS and STIG.
|
28
|
+
# @return [Symbol] The type of control ID
|
29
|
+
def control_id_type(id, framework = nil)
|
30
|
+
case framework
|
31
|
+
when 'cis'
|
32
|
+
cis_control_id_type(id)
|
33
|
+
when 'stig'
|
34
|
+
stig_control_id_type(id)
|
35
|
+
else
|
36
|
+
# stig_control_id_type can return nil, cis_control_id_type will return 'title'
|
37
|
+
stig_control_id_type(id) || cis_control_id_type(id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# The type of control ID used in the CIS mapping data, resource data, or control configs.
|
42
|
+
# @param id [String] The control ID
|
43
|
+
# @return (see #control_id_type)
|
44
|
+
def cis_control_id_type(id)
|
45
|
+
case id
|
46
|
+
when /^([0-9]\.)+/
|
47
|
+
:number
|
48
|
+
when /^[A-Za-z0-9_]+$/
|
49
|
+
:hiera_title
|
50
|
+
when /^c[0-9_]+$/
|
51
|
+
:hiera_title_num
|
52
|
+
else
|
53
|
+
:title
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# The type of control ID used in the STIG mapping data, resource data, or control configs.
|
58
|
+
# @param id [String] The control ID
|
59
|
+
# @return (see #control_id_type)
|
60
|
+
def stig_control_id_type(id)
|
61
|
+
case id
|
62
|
+
when /^V-\d{6}$/
|
63
|
+
:vulnid
|
64
|
+
when /^SV-\d{6}r\d{6}_rule$/
|
65
|
+
:ruleid
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
20
71
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cem_spec_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- abide-team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Provides helper methods, classes, modules, etc. for RSpec testing the
|
14
14
|
CEM modules
|