abide_dev_utils 0.4.1 → 0.6.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/README.md +34 -0
- data/abide_dev_utils.gemspec +4 -1
- data/lib/abide_dev_utils/cli/abstract.rb +2 -0
- data/lib/abide_dev_utils/cli/comply.rb +99 -0
- data/lib/abide_dev_utils/cli/jira.rb +4 -2
- data/lib/abide_dev_utils/cli/puppet.rb +118 -11
- data/lib/abide_dev_utils/cli.rb +2 -0
- data/lib/abide_dev_utils/comply.rb +441 -0
- data/lib/abide_dev_utils/config.rb +24 -1
- data/lib/abide_dev_utils/errors/comply.rb +13 -0
- data/lib/abide_dev_utils/errors/gcloud.rb +27 -0
- data/lib/abide_dev_utils/errors/ppt.rb +12 -0
- data/lib/abide_dev_utils/errors.rb +2 -0
- data/lib/abide_dev_utils/gcloud.rb +21 -0
- data/lib/abide_dev_utils/jira.rb +17 -0
- data/lib/abide_dev_utils/mixins.rb +16 -0
- data/lib/abide_dev_utils/ppt/class_utils.rb +184 -0
- data/lib/abide_dev_utils/ppt/coverage.rb +2 -3
- data/lib/abide_dev_utils/ppt.rb +135 -49
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/cis/hiera.rb +71 -66
- data/lib/abide_dev_utils/xccdf/utils.rb +85 -0
- data/lib/abide_dev_utils/xccdf.rb +5 -0
- data/lib/abide_dev_utils.rb +1 -0
- metadata +55 -6
- data/lib/abide_dev_utils/utils/general.rb +0 -9
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'abide_dev_utils/errors/ppt'
|
6
|
+
|
7
|
+
module AbideDevUtils
|
8
|
+
module Ppt
|
9
|
+
module ClassUtils
|
10
|
+
include AbideDevUtils::Errors::Ppt
|
11
|
+
|
12
|
+
CLASS_NAME_PATTERN = /\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/.freeze
|
13
|
+
CLASS_NAME_CAPTURE_PATTERN = /\A^class (?<class_name>([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*).*\Z/.freeze
|
14
|
+
|
15
|
+
# Validates a Puppet class name
|
16
|
+
# @param name [String] Puppet class name
|
17
|
+
# @return [Boolean] Is the name a valid Puppet class name
|
18
|
+
def self.valid_class_name?(name)
|
19
|
+
name.match?(CLASS_NAME_PATTERN)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Takes a full Puppet class name and returns the path
|
23
|
+
# of the class file. This command must be run from the
|
24
|
+
# root module directory if validate_path is true.
|
25
|
+
# @param class_name [String] full Puppet class name
|
26
|
+
# @return [String] path to class file
|
27
|
+
def self.path_from_class_name(class_name)
|
28
|
+
parts = class_name.split('::')
|
29
|
+
parts[-1] = "#{parts[-1]}.pp"
|
30
|
+
File.expand_path(File.join('manifests', parts[1..-1]))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the namespaced class name from a file path
|
34
|
+
# @param class_path [String] the path to the Puppet class
|
35
|
+
# @return [String] the namespaced class name
|
36
|
+
def self.class_name_from_path(class_path)
|
37
|
+
parts = class_path.split(File::SEPARATOR).map { |x| x == '' ? File::SEPARATOR : x }
|
38
|
+
module_root_idx = parts.find_index('manifests') - 1
|
39
|
+
module_root = parts[module_root_idx].split('-')[-1]
|
40
|
+
namespaces = parts[(module_root_idx + 2)..-2].join('::') # add 2 to module root idx to skip manifests dir
|
41
|
+
class_name = parts[-1].delete_suffix('.pp')
|
42
|
+
[module_root, namespaces, class_name].join('::')
|
43
|
+
end
|
44
|
+
|
45
|
+
# Takes a path to a Puppet file and extracts the class name from the class declaration in the file.
|
46
|
+
# This differs from class_name_from_path because we actually read the class file and search
|
47
|
+
# the code for a class declaration to get the class name instead of just using the path
|
48
|
+
# to construct a valid Puppet class name.
|
49
|
+
# @param path [String] the path to a Puppet file
|
50
|
+
# @return [String] the Puppet class name
|
51
|
+
# @raise [ClassDeclarationNotFoundError] if there is not class declaration in the file
|
52
|
+
def self.class_name_from_declaration(path)
|
53
|
+
File.readlines(path).each do |line|
|
54
|
+
next unless line.match?(/^class /)
|
55
|
+
|
56
|
+
return CLASS_NAME_CAPTURE_PATTERN.match(line)['class_name']
|
57
|
+
end
|
58
|
+
raise ClassDeclarationNotFoundError, "Path:#{path}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Renames a file by file move. Ensures destination path exists before moving.
|
62
|
+
# @param from_path [String] path of the original file
|
63
|
+
# @param to_path [String] path of the new file
|
64
|
+
# @param verbose [Boolean] Sets verbose mode on file operations
|
65
|
+
# @param force [Boolean] If true, file move file overwrite existing files
|
66
|
+
def self.rename_class_file(from_path, to_path, **kwargs)
|
67
|
+
verbose = kwargs.fetch(:verbose, false)
|
68
|
+
force = kwargs.fetch(:force, false)
|
69
|
+
FileUtils.mkdir_p(File.dirname(to_path), verbose: verbose)
|
70
|
+
FileUtils.mv(from_path, to_path, verbose: verbose, force: force)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Renames a Puppet class in the class declaration of the given file
|
74
|
+
# @param from [String] the original class name
|
75
|
+
# @param to [String] the new class name
|
76
|
+
# @param file_path [String] the path to the class file
|
77
|
+
# @param verbose [Boolean] Sets verbose mode on file operations
|
78
|
+
# @param force [Boolean] If true, file move file overwrite existing files
|
79
|
+
# @raise [ClassDeclarationNotFoundError] if the class file does not contain the from class declaration
|
80
|
+
def self.rename_puppet_class_declaration(from, to, file_path, **kwargs)
|
81
|
+
verbose = kwargs.fetch(:verbose, false)
|
82
|
+
force = kwargs.fetch(:force, false)
|
83
|
+
temp_file = Tempfile.new
|
84
|
+
renamed = false
|
85
|
+
begin
|
86
|
+
File.readlines(file_path).each do |line|
|
87
|
+
if line.match?(/^class #{from}.*/)
|
88
|
+
line.gsub!(/^class #{from}/, "class #{to}")
|
89
|
+
renamed = true
|
90
|
+
end
|
91
|
+
temp_file.puts line
|
92
|
+
end
|
93
|
+
raise ClassDeclarationNotFoundError, "File:#{file_path},Declaration:class #{from}" unless renamed
|
94
|
+
|
95
|
+
temp_file.close
|
96
|
+
FileUtils.mv(temp_file.path, file_path, verbose: verbose, force: force)
|
97
|
+
ensure
|
98
|
+
temp_file.close
|
99
|
+
temp_file.unlink
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Determines if a Puppet class name is mismatched by constructing a class name from
|
104
|
+
# a path to a Puppet file and extracting the class name from the class declaration
|
105
|
+
# inside the file. This is useful to determine if a Puppet class file breaks the
|
106
|
+
# autoload path pattern.
|
107
|
+
# @param path [String] path to a Puppet class file
|
108
|
+
# @return [Boolean] if the actual class name and path-constructed class name match
|
109
|
+
def self.mismatched_class_declaration?(path)
|
110
|
+
class_name_from_path(path) != class_name_from_declaration(path)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Finds all Puppet classes in the given directory that have class declarations
|
114
|
+
# that do not adhere to the autoload path pattern.
|
115
|
+
# @param class_dir [String] path to a directory containing Puppet class files
|
116
|
+
# @return [Array] paths to all Puppet class files with mismatched class names
|
117
|
+
def self.find_all_mismatched_class_declarations(class_dir)
|
118
|
+
mismatched = []
|
119
|
+
Dir[File.join(File.expand_path(class_dir), '*.pp')].each do |class_file|
|
120
|
+
mismatched << class_file if mismatched_class_declaration?(class_file)
|
121
|
+
end
|
122
|
+
mismatched.sort
|
123
|
+
end
|
124
|
+
|
125
|
+
# Given a directory holding Puppet manifests, returns
|
126
|
+
# the full namespace for all classes in that directory.
|
127
|
+
# @param puppet_class_dir [String] path to a dir containing Puppet manifests
|
128
|
+
# @return [String] The namespace for all classes in manifests in the dir
|
129
|
+
def self.find_class_namespace(puppet_class_dir)
|
130
|
+
path = Pathname.new(puppet_class_dir)
|
131
|
+
mod_root = nil
|
132
|
+
ns_parts = []
|
133
|
+
found_manifests = false
|
134
|
+
path.ascend do |p|
|
135
|
+
if found_manifests
|
136
|
+
mod_root = find_mod_root(p)
|
137
|
+
break
|
138
|
+
end
|
139
|
+
if File.basename(p) == 'manifests'
|
140
|
+
found_manifests = true
|
141
|
+
next
|
142
|
+
else
|
143
|
+
ns_parts << File.basename(p)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
"#{mod_root}::#{ns_parts.reverse.join('::')}::"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Given a Pathname object of the 'manifests' directory in a Puppet module,
|
150
|
+
# determines the module namespace root. Does this by consulting
|
151
|
+
# metadata.json, if it exists, or by using the parent directory name.
|
152
|
+
# @param pathname [Pathname] A Pathname object of the module's manifests dir
|
153
|
+
# @return [String] The module's namespace root
|
154
|
+
def self.find_mod_root(pathname)
|
155
|
+
metadata_file = nil
|
156
|
+
pathname.entries.each do |e|
|
157
|
+
metadata_file = "#{pathname}/metadata.json" if File.basename(e) == 'metadata.json'
|
158
|
+
end
|
159
|
+
if metadata_file.nil?
|
160
|
+
File.basename(p)
|
161
|
+
else
|
162
|
+
File.open(metadata_file) do |f|
|
163
|
+
file = JSON.parse(f.read)
|
164
|
+
File.basename(p) unless file.key?('name')
|
165
|
+
file['name'].split('-')[-1]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Array] An array of frozen arrays where each sub-array's
|
171
|
+
# index 0 is class_name and index 1 is the full path to the file.
|
172
|
+
def self.find_all_classes_and_paths(puppet_class_dir)
|
173
|
+
all_cap = []
|
174
|
+
Dir.each_child(puppet_class_dir) do |c|
|
175
|
+
path = "#{puppet_class_dir}/#{c}"
|
176
|
+
next if File.directory?(path) || File.extname(path) != '.pp'
|
177
|
+
|
178
|
+
all_cap << [File.basename(path, '.pp'), path].freeze
|
179
|
+
end
|
180
|
+
all_cap
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -4,16 +4,15 @@ require 'json'
|
|
4
4
|
require 'pathname'
|
5
5
|
require 'yaml'
|
6
6
|
require 'puppet_pal'
|
7
|
-
require 'abide_dev_utils/ppt'
|
7
|
+
require 'abide_dev_utils/ppt/class_utils'
|
8
8
|
|
9
9
|
module AbideDevUtils
|
10
10
|
module Ppt
|
11
11
|
class CoverageReport
|
12
|
-
include AbideDevUtils::Ppt
|
13
12
|
def self.generate(puppet_class_dir, hiera_path, profile = nil)
|
14
13
|
coverage = {}
|
15
14
|
coverage['classes'] = {}
|
16
|
-
all_cap =
|
15
|
+
all_cap = ClassUtils.find_all_classes_and_paths(puppet_class_dir)
|
17
16
|
invalid_classes = find_invalid_classes(all_cap)
|
18
17
|
valid_classes = find_valid_classes(all_cap, invalid_classes)
|
19
18
|
coverage['classes']['invalid'] = invalid_classes
|
data/lib/abide_dev_utils/ppt.rb
CHANGED
@@ -1,66 +1,152 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'abide_dev_utils/
|
4
|
-
require 'abide_dev_utils/
|
3
|
+
require 'abide_dev_utils/output'
|
4
|
+
require 'abide_dev_utils/validate'
|
5
|
+
require 'abide_dev_utils/errors'
|
6
|
+
require 'abide_dev_utils/ppt/class_utils'
|
5
7
|
|
6
8
|
module AbideDevUtils
|
7
9
|
module Ppt
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# @param
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
# Renames a Puppet class by renaming the class declaration and class file
|
11
|
+
# @param from [String] fully-namespaced existing Puppet class name
|
12
|
+
# @param to [String] fully-namespaced new Puppet class name
|
13
|
+
def self.rename_puppet_class(from, to, **kwargs)
|
14
|
+
from_path = ClassUtils.path_from_class_name(from)
|
15
|
+
to_path = ClassUtils.path_from_class_name(to)
|
16
|
+
file_path = kwargs.fetch(:declaration_in_to_file, false) ? to_path : from_path
|
17
|
+
raise ClassFileNotFoundError, "Path:#{file_path}" if !File.file?(file_path) && kwargs.fetch(:validate_path, true)
|
18
|
+
|
19
|
+
rename_puppet_class_declaration(from, to, file_path, **kwargs)
|
20
|
+
AbideDevUtils::Output.simple("Renamed #{from} to #{to} at #{file_path}.")
|
21
|
+
return unless kwargs.fetch(:declaration_only, false)
|
22
|
+
|
23
|
+
rename_class_file(from_path, to_path, **kwargs)
|
24
|
+
AbideDevUtils::Output.simple("Renamed file #{from_path} to #{to_path}.")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.audit_class_names(dir, **kwargs)
|
28
|
+
mismatched = ClassUtils.find_all_mismatched_class_declarations(dir)
|
29
|
+
outfile = kwargs.key?(:file) ? File.open(kwargs[:file], 'a') : nil
|
30
|
+
quiet = kwargs.fetch(:quiet, false)
|
31
|
+
mismatched.each do |class_file|
|
32
|
+
AbideDevUtils::Output.simple("Mismatched class name in file #{class_file}") unless quiet
|
33
|
+
outfile << "MISMATCHED_CLASS_NAME: #{class_file}\n" unless outfile.nil?
|
28
34
|
end
|
29
|
-
|
35
|
+
outfile&.close
|
36
|
+
AbideDevUtils::Output.simple("Found #{mismatched.length} mismatched classes in #{dir}.") unless quiet
|
37
|
+
ensure
|
38
|
+
outfile&.close
|
30
39
|
end
|
31
40
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
metadata_file = "#{pathname}/metadata.json" if File.basename(e) == 'metadata.json'
|
41
|
+
def self.fix_class_names_file_rename(dir, **kwargs)
|
42
|
+
mismatched = ClassUtils.find_all_mismatched_class_declarations(dir)
|
43
|
+
progress = AbideDevUtils::Output.progress(title: 'Renaming files', total: mismatched.length)
|
44
|
+
mismatched.each do |class_path|
|
45
|
+
should = ClassUtils.path_from_class_name(class_name_from_declaration(class_path))
|
46
|
+
ClassUtils.rename_class_file(class_path, should, **kwargs)
|
47
|
+
progress.increment
|
48
|
+
AbideDevUtils::Output.simple("Renamed file #{class_path} to #{should}...") if kwargs.fetch(:verbose, false)
|
41
49
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
AbideDevUtils::Output.simple('Successfully fixed all classes.')
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.fix_class_names_class_rename(dir, **kwargs)
|
54
|
+
mismatched = ClassUtils.find_all_mismatched_class_declarations(dir)
|
55
|
+
progress = AbideDevUtils::Output.progress(title: 'Renaming classes', total: mismatched.length)
|
56
|
+
mismatched.each do |class_path|
|
57
|
+
current = ClassUtils.class_name_from_declaration(class_path)
|
58
|
+
should = ClassUtils.class_name_from_path(class_path)
|
59
|
+
ClassUtils.rename_puppet_class_declaration(current, should, class_path, **kwargs)
|
60
|
+
progress.increment
|
61
|
+
AbideDevUtils::Output.simple("Renamed #{from} to #{to} at #{file_path}...") if kwargs.fetch(:verbose, false)
|
62
|
+
end
|
63
|
+
AbideDevUtils::Output.simple('Successfully fixed all classes.')
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.generate_coverage_report(puppet_class_dir, hiera_path, profile = nil)
|
67
|
+
require 'abide_dev_utils/ppt/coverage'
|
68
|
+
CoverageReport.generate(puppet_class_dir, hiera_path, profile)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.build_new_object(type, name, opts)
|
72
|
+
require 'abide_dev_utils/ppt/new_obj'
|
73
|
+
AbideDevUtils::Ppt::NewObjectBuilder.new(
|
74
|
+
type,
|
75
|
+
name,
|
76
|
+
opts: opts,
|
77
|
+
vars: opts.fetch(:vars, '').split(',').map { |i| i.split('=') }.to_h # makes the str a hash
|
78
|
+
).build
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.add_cis_comment(path, xccdf, number_format: false)
|
82
|
+
require 'abide_dev_utils/xccdf'
|
83
|
+
utils = AbideDevUtils::XCCDF::UtilsObject
|
84
|
+
parsed_xccdf = utils.parse(xccdf)
|
85
|
+
return add_cis_comment_to_all(path, parsed_xccdf, utils, number_format: number_format) if File.directory?(path)
|
86
|
+
return add_cis_comment_to_single(path, parsed_xccdf, utils, number_format: number_format) if File.file?(path)
|
87
|
+
|
88
|
+
raise AbideDevUtils::Errors::FileNotFoundError, path
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.add_cis_comment_to_single(path, xccdf, utils, number_format: false)
|
92
|
+
write_cis_comment_to_file(
|
93
|
+
path,
|
94
|
+
cis_recommendation_comment(
|
95
|
+
path,
|
96
|
+
utils.all_cis_recommendations(xccdf),
|
97
|
+
number_format,
|
98
|
+
utils
|
99
|
+
)
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.add_cis_comment_to_all(path, xccdf, utils, number_format: false)
|
104
|
+
comments = {}
|
105
|
+
recommendations = utils.all_cis_recommendations(xccdf)
|
106
|
+
Dir[File.join(path, '*.pp')].each do |puppet_file|
|
107
|
+
comment = cis_recommendation_comment(puppet_file, recommendations, number_format, utils)
|
108
|
+
comments[puppet_file] = comment unless comment.nil?
|
109
|
+
end
|
110
|
+
comments.each do |key, value|
|
111
|
+
write_cis_comment_to_file(key, value)
|
50
112
|
end
|
113
|
+
AbideDevUtils::Output.simple('Successfully added comments.')
|
51
114
|
end
|
52
115
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
116
|
+
def self.write_cis_comment_to_file(path, comment)
|
117
|
+
require 'tempfile'
|
118
|
+
tempfile = Tempfile.new
|
119
|
+
begin
|
120
|
+
File.open(tempfile, 'w') do |nf|
|
121
|
+
nf.write("#{comment}\n")
|
122
|
+
File.foreach(path) do |line|
|
123
|
+
next if line.match?(/#{comment}/)
|
124
|
+
|
125
|
+
nf << line
|
126
|
+
end
|
127
|
+
end
|
128
|
+
File.rename(path, "#{path}.old")
|
129
|
+
tempfile.close
|
130
|
+
File.rename(tempfile.path, path)
|
131
|
+
File.delete("#{path}.old")
|
132
|
+
AbideDevUtils::Output.simple("Added CIS recomendation comment to #{path}...")
|
133
|
+
ensure
|
134
|
+
tempfile.close
|
135
|
+
tempfile.unlink
|
136
|
+
end
|
137
|
+
end
|
60
138
|
|
61
|
-
|
139
|
+
def self.cis_recommendation_comment(puppet_file, recommendations, number_format, utils)
|
140
|
+
reco_text = utils.find_cis_recommendation(
|
141
|
+
File.basename(puppet_file, '.pp'),
|
142
|
+
recommendations,
|
143
|
+
number_format: number_format
|
144
|
+
)
|
145
|
+
if reco_text.nil?
|
146
|
+
AbideDevUtils::Output.simple("Could not find recommendation text for #{puppet_file}...")
|
147
|
+
return nil
|
62
148
|
end
|
63
|
-
|
149
|
+
"# #{reco_text}"
|
64
150
|
end
|
65
151
|
end
|
66
152
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'nokogiri'
|
5
5
|
require 'abide_dev_utils/errors'
|
6
|
+
require 'abide_dev_utils/xccdf/utils'
|
6
7
|
|
7
8
|
module AbideDevUtils
|
8
9
|
module XCCDF
|
@@ -12,21 +13,23 @@ module AbideDevUtils
|
|
12
13
|
# @!attribute [r] version
|
13
14
|
# @!attribute [r] yaml_title
|
14
15
|
class Hiera
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
16
|
+
include AbideDevUtils::XCCDF::Utils
|
17
|
+
|
18
|
+
# CONTROL_PREFIX = /^[\d.]+_/.freeze
|
19
|
+
# UNDERSCORED = /(\s|\(|\)|-|\.)/.freeze
|
20
|
+
# XPATHS = {
|
21
|
+
# benchmark: {
|
22
|
+
# all: 'xccdf:Benchmark',
|
23
|
+
# title: 'xccdf:Benchmark/xccdf:title',
|
24
|
+
# version: 'xccdf:Benchmark/xccdf:version'
|
25
|
+
# },
|
26
|
+
# profiles: {
|
27
|
+
# all: 'xccdf:Benchmark/xccdf:Profile',
|
28
|
+
# relative_title: './xccdf:title',
|
29
|
+
# relative_select: './xccdf:select'
|
30
|
+
# }
|
31
|
+
# }.freeze
|
32
|
+
# NEXT_GEN_WINDOWS = /(next_generation_windows_security)/.freeze
|
30
33
|
|
31
34
|
attr_reader :title, :version
|
32
35
|
|
@@ -37,15 +40,15 @@ module AbideDevUtils
|
|
37
40
|
# the top-level key.
|
38
41
|
def initialize(xccdf_file, parent_key_prefix: nil, num: false)
|
39
42
|
@doc = parse(xccdf_file)
|
40
|
-
@title = xpath(
|
41
|
-
@version = xpath(
|
42
|
-
@profiles = xpath(
|
43
|
+
@title = xpath(CIS_XPATHS[:benchmark][:title]).children.to_s
|
44
|
+
@version = xpath(CIS_XPATHS[:benchmark][:version]).children.to_s
|
45
|
+
@profiles = xpath(CIS_XPATHS[:profiles][:all])
|
43
46
|
@parent_key = make_parent_key(@doc, parent_key_prefix)
|
44
|
-
@hash = make_hash(@doc,
|
47
|
+
@hash = make_hash(@doc, number_format: num)
|
45
48
|
end
|
46
49
|
|
47
50
|
def yaml_title
|
48
|
-
|
51
|
+
normalize_string(@title)
|
49
52
|
end
|
50
53
|
|
51
54
|
# Convert the Hiera object to a hash
|
@@ -63,8 +66,8 @@ module AbideDevUtils
|
|
63
66
|
# Convert the Hiera object to YAML string
|
64
67
|
# @return [String] YAML-formatted string
|
65
68
|
def to_yaml
|
66
|
-
yh = @hash
|
67
|
-
|
69
|
+
yh = @hash.transform_keys do |k|
|
70
|
+
[@parent_key, k].join('::').strip
|
68
71
|
end
|
69
72
|
yh.to_yaml
|
70
73
|
end
|
@@ -92,64 +95,66 @@ module AbideDevUtils
|
|
92
95
|
|
93
96
|
attr_accessor :doc, :hash, :parent_key, :profiles
|
94
97
|
|
95
|
-
# Accepts a path to an xccdf xml file and returns a parsed Nokogiri object of the file
|
96
|
-
# @param xccdf_file [String] path to an xccdf xml file
|
97
|
-
# @return [Nokogiri::Node] A Nokogiri node object of the XML document
|
98
|
-
def parse(xccdf_file)
|
99
|
-
|
98
|
+
# # Accepts a path to an xccdf xml file and returns a parsed Nokogiri object of the file
|
99
|
+
# # @param xccdf_file [String] path to an xccdf xml file
|
100
|
+
# # @return [Nokogiri::Node] A Nokogiri node object of the XML document
|
101
|
+
# def parse(xccdf_file)
|
102
|
+
# raise AbideDevUtils::Errors::FileNotFoundError, xccdf_file unless File.file?(xccdf_file)
|
100
103
|
|
101
|
-
|
102
|
-
end
|
104
|
+
# Nokogiri.XML(File.open(xccdf_file))
|
105
|
+
# end
|
103
106
|
|
104
|
-
def make_hash(doc,
|
105
|
-
hash = {
|
107
|
+
def make_hash(doc, number_format: false)
|
108
|
+
hash = { 'title' => @title, 'version' => @version }
|
106
109
|
profiles = doc.xpath('xccdf:Benchmark/xccdf:Profile')
|
107
110
|
profiles.each do |p|
|
108
111
|
title = normalize_profile_name(p.xpath('./xccdf:title').children.to_s)
|
109
|
-
hash[
|
112
|
+
hash[title.to_s] = []
|
110
113
|
selects = p.xpath('./xccdf:select')
|
111
114
|
selects.each do |s|
|
112
|
-
hash[
|
115
|
+
hash[title.to_s] << normalize_control_name(s['idref'].to_s, number_format: number_format)
|
113
116
|
end
|
114
117
|
end
|
115
118
|
hash
|
116
119
|
end
|
117
120
|
|
118
|
-
def normalize_str(str)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
121
|
+
# def normalize_str(str)
|
122
|
+
# nstr = str.downcase
|
123
|
+
# nstr.gsub!(/[^a-z0-9]$/, '')
|
124
|
+
# nstr.gsub!(/^[^a-z]/, '')
|
125
|
+
# nstr.gsub!(/^(l1_|l2_|ng_)/, '')
|
126
|
+
# nstr.delete!('(/|\\|\+)')
|
127
|
+
# nstr.gsub!(UNDERSCORED, '_')
|
128
|
+
# nstr.strip!
|
129
|
+
# nstr
|
130
|
+
# end
|
131
|
+
|
132
|
+
# def normalize_profile_name(prof)
|
133
|
+
# prof_name = normalize_str("profile_#{prof}")
|
134
|
+
# prof_name.gsub!(NEXT_GEN_WINDOWS, 'ngws')
|
135
|
+
# prof_name.strip!
|
136
|
+
# prof_name
|
137
|
+
# end
|
138
|
+
|
139
|
+
# def normalize_ctrl_name(ctrl, num)
|
140
|
+
# return num_normalize_ctrl(ctrl) if num
|
141
|
+
|
142
|
+
# name_normalize_ctrl(ctrl)
|
143
|
+
# end
|
144
|
+
|
145
|
+
# def name_normalize_ctrl(ctrl)
|
146
|
+
# new_ctrl = ctrl.split('benchmarks_rule_')[-1].gsub(CONTROL_PREFIX, '')
|
147
|
+
# normalize_str(new_ctrl)
|
148
|
+
# end
|
149
|
+
|
150
|
+
# def num_normalize_ctrl(ctrl)
|
151
|
+
# part = ctrl.split('benchmarks_rule_')[-1]
|
152
|
+
# numpart = CONTROL_PREFIX.match(part).to_s.chop.gsub(UNDERSCORED, '_')
|
153
|
+
# "c#{numpart}"
|
154
|
+
# end
|
150
155
|
|
151
156
|
def make_parent_key(doc, prefix)
|
152
|
-
doc_title =
|
157
|
+
doc_title = normalize_string(doc.xpath(CIS_XPATHS[:benchmark][:title]).children.to_s)
|
153
158
|
return doc_title if prefix.nil?
|
154
159
|
|
155
160
|
sepped_prefix = prefix.end_with?('::') ? prefix : "#{prefix}::"
|