cem_spec_helper 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|