abide_dev_utils 0.6.0 → 0.8.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/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/CODEOWNERS +1 -0
- data/abide_dev_utils.gemspec +7 -6
- data/itests.rb +138 -0
- data/lib/abide_dev_utils/cli/comply.rb +23 -7
- data/lib/abide_dev_utils/cli/puppet.rb +18 -0
- data/lib/abide_dev_utils/cli/xccdf.rb +26 -7
- data/lib/abide_dev_utils/comply.rb +225 -168
- data/lib/abide_dev_utils/errors/comply.rb +4 -0
- data/lib/abide_dev_utils/errors/general.rb +5 -0
- data/lib/abide_dev_utils/errors/xccdf.rb +8 -0
- data/lib/abide_dev_utils/gcloud.rb +2 -1
- data/lib/abide_dev_utils/output.rb +7 -3
- data/lib/abide_dev_utils/ppt/api.rb +219 -0
- data/lib/abide_dev_utils/ppt/score_module.rb +162 -0
- data/lib/abide_dev_utils/ppt.rb +22 -19
- data/lib/abide_dev_utils/validate.rb +5 -1
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf.rb +565 -12
- metadata +30 -14
- data/lib/abide_dev_utils/xccdf/cis/hiera.rb +0 -166
- data/lib/abide_dev_utils/xccdf/cis.rb +0 -3
- data/lib/abide_dev_utils/xccdf/utils.rb +0 -85
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'yaml'
|
|
4
|
-
require 'nokogiri'
|
|
5
|
-
require 'abide_dev_utils/errors'
|
|
6
|
-
require 'abide_dev_utils/xccdf/utils'
|
|
7
|
-
|
|
8
|
-
module AbideDevUtils
|
|
9
|
-
module XCCDF
|
|
10
|
-
module CIS
|
|
11
|
-
# Creates a Hiera structure by parsing a CIS XCCDF benchmark
|
|
12
|
-
# @!attribute [r] title
|
|
13
|
-
# @!attribute [r] version
|
|
14
|
-
# @!attribute [r] yaml_title
|
|
15
|
-
class Hiera
|
|
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
|
|
33
|
-
|
|
34
|
-
attr_reader :title, :version
|
|
35
|
-
|
|
36
|
-
# Creates a new Hiera object
|
|
37
|
-
# @param xccdf_file [String] path to an XCCDF file
|
|
38
|
-
# @param parent_key_prefix [String] a string to be prepended to the
|
|
39
|
-
# top-level key in the Hiera structure. Useful for namespacing
|
|
40
|
-
# the top-level key.
|
|
41
|
-
def initialize(xccdf_file, parent_key_prefix: nil, num: false)
|
|
42
|
-
@doc = parse(xccdf_file)
|
|
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])
|
|
46
|
-
@parent_key = make_parent_key(@doc, parent_key_prefix)
|
|
47
|
-
@hash = make_hash(@doc, number_format: num)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def yaml_title
|
|
51
|
-
normalize_string(@title)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Convert the Hiera object to a hash
|
|
55
|
-
# @return [Hash]
|
|
56
|
-
def to_h
|
|
57
|
-
@hash
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Convert the Hiera object to a string
|
|
61
|
-
# @return [String]
|
|
62
|
-
def to_s
|
|
63
|
-
@hash.inspect
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Convert the Hiera object to YAML string
|
|
67
|
-
# @return [String] YAML-formatted string
|
|
68
|
-
def to_yaml
|
|
69
|
-
yh = @hash.transform_keys do |k|
|
|
70
|
-
[@parent_key, k].join('::').strip
|
|
71
|
-
end
|
|
72
|
-
yh.to_yaml
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# If a method gets called on the Hiera object which is not defined,
|
|
76
|
-
# this sends that method call to hash, then doc, then super.
|
|
77
|
-
def method_missing(method, *args, &block)
|
|
78
|
-
return true if ['exist?', 'exists?'].include?(method.to_s)
|
|
79
|
-
|
|
80
|
-
return @hash.send(method, *args, &block) if @hash.respond_to?(method)
|
|
81
|
-
|
|
82
|
-
return @doc.send(method, *args, &block) if @doc.respond_to?(method)
|
|
83
|
-
|
|
84
|
-
super(method, *args, &block)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Checks the respond_to? of hash, doc, or super
|
|
88
|
-
def respond_to_missing?(method_name, include_private = false)
|
|
89
|
-
return true if ['exist?', 'exists?'].include?(method_name.to_s)
|
|
90
|
-
|
|
91
|
-
@hash || @doc || super
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
private
|
|
95
|
-
|
|
96
|
-
attr_accessor :doc, :hash, :parent_key, :profiles
|
|
97
|
-
|
|
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)
|
|
103
|
-
|
|
104
|
-
# Nokogiri.XML(File.open(xccdf_file))
|
|
105
|
-
# end
|
|
106
|
-
|
|
107
|
-
def make_hash(doc, number_format: false)
|
|
108
|
-
hash = { 'title' => @title, 'version' => @version }
|
|
109
|
-
profiles = doc.xpath('xccdf:Benchmark/xccdf:Profile')
|
|
110
|
-
profiles.each do |p|
|
|
111
|
-
title = normalize_profile_name(p.xpath('./xccdf:title').children.to_s)
|
|
112
|
-
hash[title.to_s] = []
|
|
113
|
-
selects = p.xpath('./xccdf:select')
|
|
114
|
-
selects.each do |s|
|
|
115
|
-
hash[title.to_s] << normalize_control_name(s['idref'].to_s, number_format: number_format)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
hash
|
|
119
|
-
end
|
|
120
|
-
|
|
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
|
|
155
|
-
|
|
156
|
-
def make_parent_key(doc, prefix)
|
|
157
|
-
doc_title = normalize_string(doc.xpath(CIS_XPATHS[:benchmark][:title]).children.to_s)
|
|
158
|
-
return doc_title if prefix.nil?
|
|
159
|
-
|
|
160
|
-
sepped_prefix = prefix.end_with?('::') ? prefix : "#{prefix}::"
|
|
161
|
-
"#{sepped_prefix.chomp}#{doc_title}"
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'nokogiri'
|
|
4
|
-
require 'abide_dev_utils/validate'
|
|
5
|
-
require 'pry'
|
|
6
|
-
|
|
7
|
-
module AbideDevUtils
|
|
8
|
-
module XCCDF
|
|
9
|
-
module Utils
|
|
10
|
-
CONTROL_PREFIX = /^[\d.]+_/.freeze
|
|
11
|
-
UNDERSCORED = /(\s|\(|\)|-|\.)/.freeze
|
|
12
|
-
CIS_NEXT_GEN_WINDOWS = /(next_generation_windows_security)/.freeze
|
|
13
|
-
CIS_XPATHS = {
|
|
14
|
-
benchmark: {
|
|
15
|
-
all: 'xccdf:Benchmark',
|
|
16
|
-
title: 'xccdf:Benchmark/xccdf:title',
|
|
17
|
-
version: 'xccdf:Benchmark/xccdf:version'
|
|
18
|
-
},
|
|
19
|
-
profiles: {
|
|
20
|
-
all: 'xccdf:Benchmark/xccdf:Profile',
|
|
21
|
-
relative_title: './xccdf:title',
|
|
22
|
-
relative_select: './xccdf:select'
|
|
23
|
-
}
|
|
24
|
-
}.freeze
|
|
25
|
-
|
|
26
|
-
def parse(xccdf_file)
|
|
27
|
-
AbideDevUtils::Validate.file(xccdf_file)
|
|
28
|
-
File.open(xccdf_file) { |f| Nokogiri::XML(f) }
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def normalize_string(str)
|
|
32
|
-
nstr = str.downcase
|
|
33
|
-
nstr.gsub!(/[^a-z0-9]$/, '')
|
|
34
|
-
nstr.gsub!(/^[^a-z]/, '')
|
|
35
|
-
nstr.gsub!(/^(l1_|l2_|ng_)/, '')
|
|
36
|
-
nstr.delete!('(/|\\|\+)')
|
|
37
|
-
nstr.gsub!(UNDERSCORED, '_')
|
|
38
|
-
nstr.strip!
|
|
39
|
-
nstr
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def normalize_profile_name(prof)
|
|
43
|
-
prof_name = normalize_string("profile_#{prof}")
|
|
44
|
-
prof_name.gsub!(NEXT_GEN_WINDOWS, 'ngws')
|
|
45
|
-
prof_name.strip!
|
|
46
|
-
prof_name
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def normalize_control_name(control, number_format: false)
|
|
50
|
-
return number_normalize_control(control) if number_format
|
|
51
|
-
|
|
52
|
-
name_normalize_control(control)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def name_normalize_control(control)
|
|
56
|
-
new_ctrl = control.split('benchmarks_rule_')[-1].gsub(CONTROL_PREFIX, '')
|
|
57
|
-
normalize_string(new_ctrl)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def number_normalize_control(control)
|
|
61
|
-
part = control.split('benchmarks_rule_')[-1]
|
|
62
|
-
numpart = CONTROL_PREFIX.match(part).to_s.chop.gsub(UNDERSCORED, '_')
|
|
63
|
-
"c#{numpart}"
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def text_normalize_control(control)
|
|
67
|
-
control = control['idref'].to_s unless control.respond_to?(:split)
|
|
68
|
-
|
|
69
|
-
control.split('benchmarks_rule_')[-1].tr('_', ' ')
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def all_cis_recommendations(parsed_xccdf)
|
|
73
|
-
parsed_xccdf.xpath('//xccdf:select').uniq
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def find_cis_recommendation(name, recommendations, number_format: false)
|
|
77
|
-
recommendations.each do |reco|
|
|
78
|
-
if normalize_control_name(reco['idref'].to_s, number_format: number_format) == name
|
|
79
|
-
return text_normalize_control(reco['idref'].to_s)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|