abide_dev_utils 0.4.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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}::"
|