abide_dev_utils 0.12.2 → 0.14.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/.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
|