abide_dev_utils 0.12.2 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/mend_ruby.yaml +39 -0
- data/Gemfile.lock +43 -29
- data/abide_dev_utils.gemspec +2 -1
- data/lib/abide_dev_utils/cem/benchmark.rb +8 -5
- data/lib/abide_dev_utils/cem/generate/coverage_report.rb +9 -7
- data/lib/abide_dev_utils/cem/generate/reference.rb +176 -8
- data/lib/abide_dev_utils/cem/validate/strings/base_validator.rb +130 -0
- data/lib/abide_dev_utils/cem/validate/strings/puppet_class_validator.rb +102 -0
- data/lib/abide_dev_utils/cem/validate/strings/puppet_defined_type_validator.rb +18 -0
- data/lib/abide_dev_utils/cem/validate/strings/validation_finding.rb +31 -0
- data/lib/abide_dev_utils/cem/validate/strings.rb +82 -0
- data/lib/abide_dev_utils/cem/validate.rb +2 -1
- data/lib/abide_dev_utils/cem.rb +1 -0
- data/lib/abide_dev_utils/cli/cem.rb +63 -5
- data/lib/abide_dev_utils/cli/jira.rb +3 -2
- data/lib/abide_dev_utils/errors/jira.rb +4 -0
- data/lib/abide_dev_utils/jira.rb +107 -50
- data/lib/abide_dev_utils/markdown.rb +4 -0
- data/lib/abide_dev_utils/output.rb +23 -9
- data/lib/abide_dev_utils/ppt/code_introspection.rb +14 -1
- data/lib/abide_dev_utils/ppt/facter_utils.rb +272 -71
- data/lib/abide_dev_utils/ppt/hiera.rb +5 -4
- data/lib/abide_dev_utils/ppt/strings.rb +183 -0
- data/lib/abide_dev_utils/ppt.rb +10 -10
- data/lib/abide_dev_utils/puppet_strings.rb +108 -0
- data/lib/abide_dev_utils/validate.rb +8 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/parser/objects.rb +1 -1
- metadata +26 -4
data/lib/abide_dev_utils/jira.rb
CHANGED
@@ -11,6 +11,7 @@ module AbideDevUtils
|
|
11
11
|
ERRORS = AbideDevUtils::Errors::Jira
|
12
12
|
COV_PARENT_SUMMARY_PREFIX = '::BENCHMARK:: '
|
13
13
|
COV_CHILD_SUMMARY_PREFIX = '::CONTROL:: '
|
14
|
+
PROGRESS_BAR_FORMAT = '%a %e %P% Created: %c of %C'
|
14
15
|
|
15
16
|
def self.project(client, project)
|
16
17
|
client.Project.find(project)
|
@@ -18,6 +19,11 @@ module AbideDevUtils
|
|
18
19
|
|
19
20
|
def self.issue(client, issue)
|
20
21
|
client.Issue.find(issue)
|
22
|
+
rescue URI::InvalidURIError
|
23
|
+
iss = client.Issue.all.find { |i| i.summary == issue }
|
24
|
+
raise ERRORS::FindIssueError, issue unless iss
|
25
|
+
|
26
|
+
iss
|
21
27
|
end
|
22
28
|
|
23
29
|
def self.myself(client)
|
@@ -25,11 +31,19 @@ module AbideDevUtils
|
|
25
31
|
end
|
26
32
|
|
27
33
|
def self.issuetype(client, id)
|
28
|
-
|
34
|
+
if id.match?(%r{^\d+$})
|
35
|
+
client.Issuetype.find(id)
|
36
|
+
else
|
37
|
+
client.Issuetype.all.find { |i| i.name == id }
|
38
|
+
end
|
29
39
|
end
|
30
40
|
|
31
41
|
def self.priority(client, id)
|
32
|
-
|
42
|
+
if id.match?(%r{^\d+$})
|
43
|
+
client.Priority.find(id)
|
44
|
+
else
|
45
|
+
client.Priority.all.find { |i| i.name == id }
|
46
|
+
end
|
33
47
|
end
|
34
48
|
|
35
49
|
def self.all_project_issues_attrs(project)
|
@@ -37,21 +51,50 @@ module AbideDevUtils
|
|
37
51
|
raw_issues.collect(&:attrs)
|
38
52
|
end
|
39
53
|
|
40
|
-
def self.
|
54
|
+
def self.add_issue_label(iss, label, dry_run: false)
|
55
|
+
return if dry_run || iss.labels.include?(label)
|
56
|
+
|
57
|
+
iss.labels << profile_summary
|
58
|
+
iss.save
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.new_issue(client, project, summary, labels: ['abide_dev_utils'], epic: nil, dry_run: false)
|
41
62
|
if dry_run
|
42
63
|
sleep(0.2)
|
43
|
-
return Dummy.new
|
64
|
+
return Dummy.new(summary)
|
44
65
|
end
|
45
66
|
fields = {}
|
46
67
|
fields['summary'] = summary
|
47
68
|
fields['project'] = project(client, project)
|
48
69
|
fields['reporter'] = myself(client)
|
49
|
-
fields['issuetype'] = issuetype(client, '
|
70
|
+
fields['issuetype'] = issuetype(client, 'Task')
|
50
71
|
fields['priority'] = priority(client, '6')
|
51
|
-
|
52
|
-
|
72
|
+
fields['labels'] = labels
|
73
|
+
epic = issue(client, epic) if epic && !epic.is_a?(JIRA::Resource::Issue)
|
74
|
+
fields['customfield_10006'] = epic.key if epic # Epic_Link
|
75
|
+
iss = client.Issue.build
|
76
|
+
raise ERRORS::CreateIssueError, iss.attrs unless iss.save({ 'fields' => fields })
|
53
77
|
|
54
|
-
|
78
|
+
iss
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.new_epic(client, project, summary, dry_run: false)
|
82
|
+
AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Creating epic '#{summary}'")
|
83
|
+
if dry_run
|
84
|
+
sleep(0.2)
|
85
|
+
return Dummy.new(summary)
|
86
|
+
end
|
87
|
+
fields = {
|
88
|
+
'summary' => summary,
|
89
|
+
'project' => project(client, project),
|
90
|
+
'reporter' => myself(client),
|
91
|
+
'issuetype' => issuetype(client, 'Epic'),
|
92
|
+
'customfield_10007' => summary, # Epic Name
|
93
|
+
}
|
94
|
+
iss = client.Issue.build
|
95
|
+
raise ERRORS::CreateEpicError, iss.attrs unless iss.save({ 'fields' => fields })
|
96
|
+
|
97
|
+
iss
|
55
98
|
end
|
56
99
|
|
57
100
|
# This should probably be threaded in the future
|
@@ -135,46 +178,55 @@ module AbideDevUtils
|
|
135
178
|
end
|
136
179
|
end
|
137
180
|
|
138
|
-
def self.new_issues_from_xccdf(client, project, xccdf_path, dry_run: false)
|
139
|
-
dr_prefix = dry_run ? 'DRY RUN: ' : ''
|
181
|
+
def self.new_issues_from_xccdf(client, project, xccdf_path, epic: nil, dry_run: false)
|
140
182
|
i_attrs = all_project_issues_attrs(project)
|
141
|
-
|
142
183
|
xccdf = AbideDevUtils::XCCDF::Benchmark.new(xccdf_path)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
184
|
+
# We need to get the actual epic Issue object, or create it if it doesn't exist
|
185
|
+
epic = if epic.nil?
|
186
|
+
new_epic_summary = "#{COV_PARENT_SUMMARY_PREFIX}#{xccdf.title}"
|
187
|
+
if summary_exist?(new_epic_summary, i_attrs)
|
188
|
+
issue(client, new_epic_summary)
|
189
|
+
else
|
190
|
+
unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new epic '#{new_epic_summary}'?")
|
191
|
+
AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
|
192
|
+
exit(0)
|
193
|
+
end
|
194
|
+
new_epic(client, project.key, new_epic_summary, dry_run: dry_run)
|
195
|
+
end
|
196
|
+
else
|
197
|
+
issue(client, epic)
|
198
|
+
end
|
199
|
+
# Now we need to find out which issues we need to create for the benchmark
|
200
|
+
# The profiles that the control belongs to will be added as an issue label
|
201
|
+
to_create = {}
|
202
|
+
summaries_from_xccdf(xccdf).each do |profile_summary, control_summaries|
|
203
|
+
control_summaries.reject { |s| summary_exist?(s, i_attrs) }.each do |control_summary|
|
204
|
+
if to_create.key?(control_summary)
|
205
|
+
to_create[control_summary] << profile_summary.split.join('_').downcase
|
206
|
+
else
|
207
|
+
to_create[control_summary] = [profile_summary.split.join('_').downcase]
|
208
|
+
end
|
149
209
|
end
|
210
|
+
end
|
150
211
|
|
151
|
-
|
152
|
-
AbideDevUtils::Output.simple("#{dr_prefix}
|
153
|
-
|
154
|
-
|
155
|
-
progress = AbideDevUtils::Output.progress(title: "#{dr_prefix}Creating Subtasks", total: nil)
|
156
|
-
control_summaries.each do |control_summary|
|
157
|
-
next if summary_exist?(control_summary, i_attrs)
|
212
|
+
unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create #{to_create.keys.count} new Jira issues?")
|
213
|
+
AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
|
214
|
+
exit(0)
|
215
|
+
end
|
158
216
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
217
|
+
progress = AbideDevUtils::Output.progress(title: "#{dr_prefix(dry_run)}Creating issues",
|
218
|
+
total: to_create.keys.count,
|
219
|
+
format: PROGRESS_BAR_FORMAT)
|
220
|
+
to_create.each do |control_summary, labels|
|
221
|
+
abrev = control_summary.length > 40 ? control_summary[0..60] : control_summary
|
222
|
+
progress.log("#{dr_prefix(dry_run)}Creating #{abrev}...")
|
223
|
+
new_issue(client, project.key, control_summary, labels: labels, epic: epic, dry_run: dry_run)
|
224
|
+
progress.increment
|
165
225
|
end
|
226
|
+
progress.finish
|
227
|
+
AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Done creating tasks in Epic '#{epic.summary}'")
|
166
228
|
end
|
167
229
|
|
168
|
-
# def self.new_issues_from_comply_report(client, project, report, dry_run: false)
|
169
|
-
# dr_prefix = dry_run ? 'DRY RUN: ' : ''
|
170
|
-
# i_attrs = all_project_issues_attrs(project)
|
171
|
-
# rep_sums = summaries_from_coverage_report(report)
|
172
|
-
# rep_sums.each do |k, v|
|
173
|
-
# next if summary_exist?(k, i_attrs)
|
174
|
-
|
175
|
-
# progress = AbideDevUtils::Output.progress(title: "#{dr_prefix}Creating Tasks", total: nil)
|
176
|
-
# v.each do |s|
|
177
|
-
|
178
230
|
def self.merge_options(options)
|
179
231
|
config.merge(options)
|
180
232
|
end
|
@@ -209,25 +261,30 @@ module AbideDevUtils
|
|
209
261
|
|
210
262
|
def self.summaries_from_xccdf(xccdf)
|
211
263
|
summaries = {}
|
212
|
-
facter_os = xccdf.facter_benchmark.join('-')
|
213
264
|
xccdf.profiles.each do |profile|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
265
|
+
sum_key = "#{profile.level}_#{profile.title}".split.join('_').downcase
|
266
|
+
summaries[sum_key] = profile.controls.collect do |control|
|
267
|
+
control_id = control.respond_to?(:vulnid) ? control.vulnid : control.number
|
268
|
+
summary = "#{control_id} - #{control.title}"
|
269
|
+
summary = "#{summary[0..251]}..." if summary.length > 255
|
219
270
|
summary
|
220
271
|
end
|
221
272
|
end
|
222
273
|
summaries
|
223
274
|
end
|
224
275
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
# end
|
276
|
+
def self.dr_prefix(dry_run)
|
277
|
+
dry_run ? 'DRY RUN: ' : ''
|
278
|
+
end
|
229
279
|
|
230
280
|
class Dummy
|
281
|
+
attr_reader :summary, :key
|
282
|
+
|
283
|
+
def initialize(summary = 'dummy summary')
|
284
|
+
@summary = summary
|
285
|
+
@key = 'DUM-111'
|
286
|
+
end
|
287
|
+
|
231
288
|
def attrs
|
232
289
|
{ 'fields' => {
|
233
290
|
'project' => 'dummy',
|
@@ -10,30 +10,44 @@ require 'abide_dev_utils/files'
|
|
10
10
|
module AbideDevUtils
|
11
11
|
module Output
|
12
12
|
FWRITER = AbideDevUtils::Files::Writer.new
|
13
|
-
def self.simple(msg, stream: $stdout)
|
14
|
-
|
13
|
+
def self.simple(msg, stream: $stdout, **_)
|
14
|
+
case msg
|
15
|
+
when Hash
|
16
|
+
stream.puts JSON.pretty_generate(msg)
|
17
|
+
else
|
18
|
+
stream.puts msg
|
19
|
+
end
|
15
20
|
end
|
16
21
|
|
17
|
-
def self.
|
22
|
+
def self.text(msg, console: false, file: nil, **_)
|
23
|
+
simple(msg) if console
|
24
|
+
FWRITER.write_text(msg, file: file) unless file.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.json(in_obj, console: false, file: nil, pretty: true, **_)
|
18
28
|
AbideDevUtils::Validate.hashable(in_obj)
|
19
29
|
json_out = pretty ? JSON.pretty_generate(in_obj) : JSON.generate(in_obj)
|
20
30
|
simple(json_out) if console
|
21
31
|
FWRITER.write_json(json_out, file: file) unless file.nil?
|
22
32
|
end
|
23
33
|
|
24
|
-
def self.yaml(in_obj, console: false, file: nil)
|
34
|
+
def self.yaml(in_obj, console: false, file: nil, stringify: false, **_)
|
25
35
|
yaml_out = if in_obj.is_a? String
|
26
36
|
in_obj
|
27
37
|
else
|
28
38
|
AbideDevUtils::Validate.hashable(in_obj)
|
29
|
-
|
30
|
-
|
39
|
+
if stringify
|
40
|
+
JSON.parse(JSON.generate(in_obj)).to_yaml
|
41
|
+
else
|
42
|
+
# Use object's #to_yaml method if it exists, convert to hash if not
|
43
|
+
in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
|
44
|
+
end
|
31
45
|
end
|
32
46
|
simple(yaml_out) if console
|
33
47
|
FWRITER.write_yaml(yaml_out, file: file) unless file.nil?
|
34
48
|
end
|
35
49
|
|
36
|
-
def self.yml(in_obj, console: false, file: nil)
|
50
|
+
def self.yml(in_obj, console: false, file: nil, **_)
|
37
51
|
AbideDevUtils::Validate.hashable(in_obj)
|
38
52
|
# Use object's #to_yaml method if it exists, convert to hash if not
|
39
53
|
yml_out = in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
|
@@ -41,8 +55,8 @@ module AbideDevUtils
|
|
41
55
|
FWRITER.write_yml(yml_out, file: file) unless file.nil?
|
42
56
|
end
|
43
57
|
|
44
|
-
def self.progress(title: 'Progress', start: 0, total: 100)
|
45
|
-
ProgressBar.create(title: title, starting_at: start, total: total)
|
58
|
+
def self.progress(title: 'Progress', start: 0, total: 100, format: nil, **_)
|
59
|
+
ProgressBar.create(title: title, starting_at: start, total: total, format: format)
|
46
60
|
end
|
47
61
|
end
|
48
62
|
end
|
@@ -10,17 +10,30 @@ module AbideDevUtils
|
|
10
10
|
attr_reader :manifest_file
|
11
11
|
|
12
12
|
def initialize(manifest_file)
|
13
|
+
@compiler = Puppet::Pal::Compiler.new(nil)
|
13
14
|
@manifest_file = File.expand_path(manifest_file)
|
14
15
|
raise ArgumentError, "File #{@manifest_file} is not a file" unless File.file?(@manifest_file)
|
15
16
|
end
|
16
17
|
|
17
18
|
def ast
|
18
|
-
@ast ||=
|
19
|
+
@ast ||= non_validating_parse_file(manifest_file)
|
19
20
|
end
|
20
21
|
|
21
22
|
def declaration
|
22
23
|
@declaration ||= Declaration.new(ast)
|
23
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# This method gets around the normal validation performed by the regular
|
29
|
+
# Puppet::Pal::Compiler#parse_file method. This is necessary because, with
|
30
|
+
# validation enabled, the parser will raise errors during parsing if the
|
31
|
+
# file contains any calls to Facter. This is due to facter being disallowed
|
32
|
+
# in Puppet when evaluating the code in a scripting context instead of catalog
|
33
|
+
# compilation, which is what we are doing here.
|
34
|
+
def non_validating_parse_file(file)
|
35
|
+
@compiler.send(:internal_evaluator).parser.parse_file(file)&.model
|
36
|
+
end
|
24
37
|
end
|
25
38
|
|
26
39
|
class Declaration
|