abide_dev_utils 0.13.0 → 0.14.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 863b7f1da9e228c425708434e5adb149a04462c6f9e7a0189799761d9420b6e1
4
- data.tar.gz: c221c833c4f6b89d80328b3e3bf2de527bb400fe8df5946af2c46d5be45d1b60
3
+ metadata.gz: 54b60da7a8a67908351bf5e2687c39e8ac76aad00cb71ad798ac0c0a195e07fe
4
+ data.tar.gz: 400a2593d83b24a0f970a52f4b6ae0434ff41975260b351c5dd5d0e52387f282
5
5
  SHA512:
6
- metadata.gz: 715413f107df1c3f52269658b124db9bad5b4113042ca9e18eb11cabd73cb3b83a57e3f2adf13591fc168dc841be1fb93d3e972ce74aafe6888b95802b644316
7
- data.tar.gz: 10eed44e5eb6969974f3426cc65c6c7a00cc988fc5a8ef733df50fafd1c395df44d7afec4b18f51c0ae42d5a36204174a88c42679a6eb3616080deccfb38a86f
6
+ metadata.gz: 3857661d49e0fd254fda83c58ac0363cad5df50342aa55eca44f05f8e1dd47214200ef8586bbec6c1f46767cd7d0528be8f089507e0a6699896971ffceaca2da
7
+ data.tar.gz: edaa3a7dc991292d51feba476cd49cf343e570ac87e0310957f4d55d43932fd3dc4ed6d72aa6f78fbc686063416e65ed0bbed03bf550fffcf63a135c3175d4af
@@ -0,0 +1,39 @@
1
+ name: mend_scan
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches:
6
+ - main
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: checkout repo content
12
+ uses: actions/checkout@v2 # checkout the repository content to github runner.
13
+ with:
14
+ fetch-depth: 1
15
+ - name: setup ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: 2.7
19
+ - name: create lock
20
+ run: bundle lock
21
+ # install java
22
+ - uses: actions/setup-java@v3
23
+ with:
24
+ distribution: 'temurin' # See 'Supported distributions' for available options
25
+ java-version: '17'
26
+ # download mend
27
+ - name: download_mend
28
+ run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar
29
+ - name: run mend
30
+ run: java -jar wss-unified-agent.jar
31
+ env:
32
+ WS_APIKEY: ${{ secrets.MEND_API_KEY }}
33
+ WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent
34
+ WS_USERKEY: ${{ secrets.MEND_TOKEN }}
35
+ WS_PRODUCTNAME: 'content-and-tooling' # I think this apply for our repo
36
+ WS_PROJECTNAME: ${{ github.event.repository.name }}
37
+ WS_FILESYSTEMSCAN: true
38
+ WS_CHECKPOLICIES: true
39
+ WS_FORCEUPDATE: true
data/Gemfile.lock CHANGED
@@ -1,15 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abide_dev_utils (0.13.0)
4
+ abide_dev_utils (0.14.1)
5
5
  amatch (~> 0.4)
6
6
  cmdparse (~> 3.0)
7
- facterdb (>= 1.18)
7
+ facterdb (>= 1.21)
8
8
  google-cloud-storage (~> 1.34)
9
9
  hashdiff (~> 1.0)
10
10
  jira-ruby (~> 2.2)
11
11
  nokogiri (~> 1.13)
12
12
  puppet (>= 6.23)
13
+ puppet-strings (>= 2.7)
13
14
  ruby-progressbar (~> 1.11)
14
15
  selenium-webdriver (~> 4.0.0.beta4)
15
16
 
@@ -144,8 +145,6 @@ GEM
144
145
  nio4r (2.5.8)
145
146
  nokogiri (1.14.1-x86_64-darwin)
146
147
  racc (~> 1.4)
147
- nokogiri (1.14.1-x86_64-linux)
148
- racc (~> 1.4)
149
148
  oauth (0.6.2)
150
149
  snaky_hash (~> 2.0)
151
150
  version_gem (~> 1.1)
@@ -169,17 +168,6 @@ GEM
169
168
  coderay (~> 1.1)
170
169
  method_source (~> 1.0)
171
170
  public_suffix (4.0.7)
172
- puppet (7.22.0)
173
- concurrent-ruby (~> 1.0, < 1.2.0)
174
- deep_merge (~> 1.0)
175
- facter (> 2.0.1, < 5)
176
- fast_gettext (>= 1.1, < 3)
177
- hiera (>= 3.2.1, < 4)
178
- locale (~> 2.1)
179
- multi_json (~> 1.10)
180
- puppet-resource_api (~> 1.5)
181
- scanf (~> 1.0)
182
- semantic_puppet (~> 1.0)
183
171
  puppet (7.22.0-universal-darwin)
184
172
  CFPropertyList (~> 2.2)
185
173
  concurrent-ruby (~> 1.0, < 1.2.0)
@@ -194,6 +182,9 @@ GEM
194
182
  semantic_puppet (~> 1.0)
195
183
  puppet-resource_api (1.8.14)
196
184
  hocon (>= 1.0)
185
+ puppet-strings (2.9.0)
186
+ rgen
187
+ yard (~> 0.9.5)
197
188
  racc (1.6.2)
198
189
  rainbow (3.1.1)
199
190
  rake (13.0.6)
@@ -204,6 +195,7 @@ GEM
204
195
  uber (< 0.2.0)
205
196
  retriable (3.1.2)
206
197
  rexml (3.2.5)
198
+ rgen (0.9.1)
207
199
  rspec (3.11.0)
208
200
  rspec-core (~> 3.11.0)
209
201
  rspec-expectations (~> 3.11.0)
@@ -270,7 +262,9 @@ GEM
270
262
  uber (0.1.0)
271
263
  unicode-display_width (2.1.0)
272
264
  version_gem (1.1.1)
273
- webrick (1.8.1)
265
+ webrick (1.7.0)
266
+ yard (0.9.28)
267
+ webrick (~> 1.7.0)
274
268
 
275
269
  PLATFORMS
276
270
  x86_64-darwin-19
@@ -35,13 +35,14 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency 'nokogiri', '~> 1.13'
36
36
  spec.add_dependency 'cmdparse', '~> 3.0'
37
37
  spec.add_dependency 'puppet', '>= 6.23'
38
+ spec.add_dependency 'puppet-strings', '>= 2.7'
38
39
  spec.add_dependency 'jira-ruby', '~> 2.2'
39
40
  spec.add_dependency 'ruby-progressbar', '~> 1.11'
40
41
  spec.add_dependency 'selenium-webdriver', '~> 4.0.0.beta4'
41
42
  spec.add_dependency 'google-cloud-storage', '~> 1.34'
42
43
  spec.add_dependency 'hashdiff', '~> 1.0'
43
44
  spec.add_dependency 'amatch', '~> 0.4'
44
- spec.add_dependency 'facterdb', '>= 1.18'
45
+ spec.add_dependency 'facterdb', '>= 1.21'
45
46
 
46
47
  # Dev dependencies
47
48
  spec.add_development_dependency 'bundler'
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'set'
4
- require 'abide_dev_utils/dot_number_comparable'
5
- require 'abide_dev_utils/errors'
6
- require 'abide_dev_utils/ppt'
7
- require 'abide_dev_utils/cem/mapping/mapper'
4
+ require_relative '../dot_number_comparable'
5
+ require_relative '../errors'
6
+ require_relative '../ppt'
7
+ require_relative 'mapping/mapper'
8
8
 
9
9
  module AbideDevUtils
10
10
  module CEM
@@ -317,7 +317,8 @@ module AbideDevUtils
317
317
  def initialize(osname, major_version, hiera_conf, module_name, framework: 'cis')
318
318
  @osname = osname
319
319
  @major_version = major_version
320
- @os_facts = AbideDevUtils::Ppt::FacterUtils.recursive_facts_for_os(@osname, @major_version)
320
+ @os_facts = AbideDevUtils::Ppt::FacterUtils::FactSets.new.find_by_fact_value_tuples(['os.name', @osname],
321
+ ['os.release.major', @major_version])
321
322
  @osfamily = @os_facts['os']['family']
322
323
  @hiera_conf = hiera_conf
323
324
  @module_name = module_name
@@ -484,6 +485,8 @@ module AbideDevUtils
484
485
  raise AbideDevUtils::Errors::ResourceDataNotFoundError, facts if rdata_files.nil? || rdata_files.empty?
485
486
 
486
487
  YAML.load_file(rdata_files[0].path)
488
+ rescue StandardError => e
489
+ require 'pry'; binding.pry
487
490
  end
488
491
  end
489
492
  end
@@ -15,14 +15,12 @@ module AbideDevUtils
15
15
  # the various compliance frameworks expect to be enforced.
16
16
  module CoverageReport
17
17
  def self.generate(format_func: :to_h, opts: {})
18
- opts = AbideDevUtils::CEM::CoverageReport::ReportOptions.new(opts)
18
+ opts = ReportOptions.new(opts)
19
19
  pupmod = AbideDevUtils::Ppt::PuppetModule.new
20
20
  benchmarks = AbideDevUtils::CEM::Benchmark.benchmarks_from_puppet_module(pupmod,
21
- ignore_all_errors: opts.ignore_all_errors)
21
+ ignore_all_errors: opts.ignore_all_errors)
22
22
  benchmarks.map do |b|
23
- AbideDevUtils::CEM::CoverageReport::BenchmarkReport.new(b)
24
- .send(report_type, **{ profile: profile, level: level })
25
- .send(format_func)
23
+ BenchmarkReport.new(b, opts).run.send(format_func)
26
24
  end
27
25
  end
28
26
 
@@ -306,11 +304,15 @@ module AbideDevUtils
306
304
 
307
305
  # Creates ReportOutput objects based on the given Benchmark
308
306
  class BenchmarkReport
309
- def initialize(benchmark, opts = AbideDevUtils::CEM::CoverageReport::ReportOptions.new)
307
+ def initialize(benchmark, opts = ReportOptions.new)
310
308
  @benchmark = benchmark
311
309
  @opts = opts
312
310
  end
313
311
 
312
+ def run
313
+ send(@opts.report_type)
314
+ end
315
+
314
316
  def controls_in_resource_data
315
317
  @controls_in_resource_data ||= find_controls_in_resource_data
316
318
  end
@@ -322,7 +324,7 @@ module AbideDevUtils
322
324
  def basic_coverage(level: @opts.level, profile: @opts.profile)
323
325
  map_type = @benchmark.map_type(controls_in_resource_data[0])
324
326
  rules_in_map = @benchmark.rules_in_map(map_type, level: level, profile: profile)
325
- AbideDevUtils::CEM::CoverageReport::ReportOutput.new(@benchmark, controls_in_resource_data, rules_in_map)
327
+ ReportOutput.new(@benchmark, controls_in_resource_data, rules_in_map)
326
328
  end
327
329
 
328
330
  # def correlated_coverage(level: @opts.level, profile: @opts.profile)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'puppet-strings'
5
+ require 'puppet-strings/yard'
4
6
  require 'shellwords'
5
7
  require 'timeout'
6
8
  require 'yaml'
@@ -31,7 +33,7 @@ module AbideDevUtils
31
33
  case data.fetch(:format, 'markdown')
32
34
  when 'markdown'
33
35
  file = data[:out_file] || 'REFERENCE.md'
34
- MarkdownGenerator.new(benchmarks, pupmod.name, file: file).generate(doc_title)
36
+ MarkdownGenerator.new(benchmarks, pupmod.name, file: file, opts: data).generate(doc_title)
35
37
  else
36
38
  raise "Format #{data[:format]} is unsupported! Only `markdown` format supported"
37
39
  end
@@ -61,31 +63,35 @@ module AbideDevUtils
61
63
  class MarkdownGenerator
62
64
  SPECIAL_CONTROL_IDS = %w[dependent cem_options cem_protected].freeze
63
65
 
64
- def initialize(benchmarks, module_name, file: 'REFERENCE.md')
66
+ def initialize(benchmarks, module_name, file: 'REFERENCE.md', opts: {})
65
67
  @benchmarks = benchmarks
66
68
  @module_name = module_name
67
69
  @file = file
70
+ @opts = opts
68
71
  @md = AbideDevUtils::Markdown.new(@file)
69
72
  end
70
73
 
71
74
  def generate(doc_title = 'Reference')
75
+ @strings = Strings.new(opts: @opts)
72
76
  md.add_title(doc_title)
73
77
  benchmarks.each do |benchmark|
74
- progress_bar = AbideDevUtils::Output.progress(title: "Generating Markdown for #{benchmark.title_key}",
75
- total: benchmark.controls.length)
78
+ unless @opts[:quiet]
79
+ progress_bar = AbideDevUtils::Output.progress(title: "Generating Markdown for #{benchmark.title_key}",
80
+ total: benchmark.controls.length)
81
+ end
76
82
  md.add_h1(benchmark.title_key)
77
83
  benchmark.controls.each do |control|
78
84
  next if SPECIAL_CONTROL_IDS.include? control.id
79
85
  next if benchmark.framework == 'stig' && control.id_map_type != 'vulnid'
80
86
 
81
- control_md = ControlMarkdown.new(control, @md, @module_name, benchmark.framework)
87
+ control_md = ControlMarkdown.new(control, @md, @strings, @module_name, benchmark.framework, opts: @opts)
82
88
  control_md.generate!
83
- progress_bar.increment
89
+ progress_bar.increment unless @opts[:quiet]
84
90
  rescue StandardError => e
85
91
  raise "Failed to generate markdown for control #{control.id}. Original message: #{e.message}"
86
92
  end
87
93
  end
88
- AbideDevUtils::Output.simple("Saving markdown to #{@file}")
94
+ AbideDevUtils::Output.simple("Saving markdown to #{@file}") unless @opts[:quiet]
89
95
  md.to_file
90
96
  end
91
97
 
@@ -96,13 +102,152 @@ module AbideDevUtils
96
102
 
97
103
  class ConfigExampleError < StandardError; end
98
104
 
105
+ # Puppet Strings reference object
106
+ class Strings
107
+ REGISTRY_TYPES = %i[
108
+ root
109
+ module
110
+ class
111
+ puppet_class
112
+ puppet_data_type
113
+ puppet_data_type_alias
114
+ puppet_defined_type
115
+ puppet_type
116
+ puppet_provider
117
+ puppet_function
118
+ puppet_task
119
+ puppet_plan
120
+ ].freeze
121
+
122
+ attr_reader :search_patterns
123
+
124
+ def initialize(search_patterns: nil, opts: {})
125
+ @search_patterns = search_patterns || PuppetStrings::DEFAULT_SEARCH_PATTERNS
126
+ @debug = opts[:debug]
127
+ @quiet = opts[:quiet]
128
+ PuppetStrings::Yard.setup!
129
+ YARD::CLI::Yardoc.run(*yard_args(@search_patterns, debug: @debug, quiet: @quiet))
130
+ end
131
+
132
+ def debug?
133
+ !!@debug
134
+ end
135
+
136
+ def quiet?
137
+ !!@quiet
138
+ end
139
+
140
+ def registry
141
+ @registry ||= YARD::Registry.all(*REGISTRY_TYPES)
142
+ end
143
+
144
+ def find_resource(resource_name)
145
+ to_h.each do |_, resources|
146
+ res = resources.find { |r| r[:name] == resource_name.to_sym }
147
+ return res if res
148
+ end
149
+ end
150
+
151
+ def puppet_classes
152
+ @puppet_classes ||= hashes_for_reg_type(:puppet_class)
153
+ end
154
+
155
+ def data_types
156
+ @data_types ||= hashes_for_reg_type(:puppet_data_types)
157
+ end
158
+
159
+ def data_type_aliases
160
+ @data_type_aliases ||= hashes_for_reg_type(:puppet_data_type_alias)
161
+ end
162
+
163
+ def defined_types
164
+ @defined_types ||= hashes_for_reg_type(:puppet_defined_type)
165
+ end
166
+
167
+ def resource_types
168
+ @resource_types ||= hashes_for_reg_type(:puppet_type)
169
+ end
170
+
171
+ def providers
172
+ @providers ||= hashes_for_reg_type(:puppet_provider)
173
+ end
174
+
175
+ def puppet_functions
176
+ @puppet_functions ||= hashes_for_reg_type(:puppet_function)
177
+ end
178
+
179
+ def puppet_tasks
180
+ @puppet_tasks ||= hashes_for_reg_type(:puppet_task)
181
+ end
182
+
183
+ def puppet_plans
184
+ @puppet_plans ||= hashes_for_reg_type(:puppet_plan)
185
+ end
186
+
187
+ def to_h
188
+ {
189
+ puppet_classes: puppet_classes,
190
+ data_types: data_types,
191
+ data_type_aliases: data_type_aliases,
192
+ defined_types: defined_types,
193
+ resource_types: resource_types,
194
+ providers: providers,
195
+ puppet_functions: puppet_functions,
196
+ puppet_tasks: puppet_tasks,
197
+ puppet_plans: puppet_plans,
198
+ }
199
+ end
200
+
201
+ private
202
+
203
+ def hashes_for_reg_type(reg_type)
204
+ all_to_h(registry.select { |i| i.type == reg_type })
205
+ end
206
+
207
+ def all_to_h(objects)
208
+ objects.sort_by(&:name).map(&:to_hash)
209
+ end
210
+
211
+ def yard_args(patterns, debug: false, quiet: false)
212
+ args = ['doc', '--no-progress', '-n']
213
+ args << '--debug' if debug && !quiet
214
+ args << '--backtrace' if debug && !quiet
215
+ args << '-q' if quiet
216
+ args << '--no-stats' if quiet
217
+ args += patterns
218
+ args
219
+ end
220
+ end
221
+
222
+ # Generates markdown for Puppet classes based on Puppet Strings JSON
223
+ # class PuppetClassMarkdown
224
+ # def initialize(puppet_classes, md, opts: {})
225
+ # @puppet_classes = puppet_classes
226
+ # @md = md
227
+ # @opts = opts
228
+ # end
229
+
230
+ # def generate!
231
+ # @puppet_classes.each do |puppet_class|
232
+ # @md.add_h2(puppet_class['name'])
233
+ # @md.add_paragraph("File(Line): `#{puppet_class['file']}(#{puppet_class['line']})`")
234
+
235
+ # private
236
+
237
+ # def doc_string_builder(puppet_class)
238
+ # return if puppet_class['docstring'].nil? || puppet_class['docstring'].empty?
239
+ # end
240
+
241
+ # Generates markdown for a control
99
242
  class ControlMarkdown
100
- def initialize(control, md, module_name, framework, formatter: nil)
243
+ def initialize(control, md, strings, module_name, framework, formatter: nil, opts: {})
101
244
  @control = control
102
245
  @md = md
246
+ @strings = strings
103
247
  @module_name = module_name
104
248
  @framework = framework
105
249
  @formatter = formatter.nil? ? TypeExprValueFormatter : formatter
250
+ @opts = opts
106
251
  @control_data = {}
107
252
  end
108
253
 
@@ -156,6 +301,27 @@ module AbideDevUtils
156
301
  " - #{@md.italic('Default:')} #{@md.code(@control_data[ctrl_param[:name]][:default])}"
157
302
  end
158
303
 
304
+ def param_description(ctrl_param)
305
+ res = if @control.resource.type == 'class'
306
+ @strings.find_resource(@control.resource.title)
307
+ else
308
+ @strings.find_resource(@control.resource.type)
309
+ end
310
+ return unless res&.key?(:docstring) && res[:docstring].key?(:tags)
311
+ return if res[:docstring][:tags].empty? || res[:docstring][:tags].none? { |x| x[:tag_name] == 'param' }
312
+
313
+ param_tag = res[:docstring][:tags].find { |x| x[:tag_name] == 'param' && x[:name] == ctrl_param[:name] }
314
+ if param_tag.nil? || param_tag[:text].nil? || param_tag[:text].chomp.empty?
315
+ if @opts[:strict]
316
+ raise "No description found for parameter #{ctrl_param[:name]} in resource #{@control.resource.title}"
317
+ end
318
+
319
+ return
320
+ end
321
+
322
+ " - #{param_tag[:text]}"
323
+ end
324
+
159
325
  def control_params_builder
160
326
  return unless control_has_valid_params?
161
327
 
@@ -164,6 +330,8 @@ module AbideDevUtils
164
330
  collection.each do |hsh|
165
331
  rparam = resource_param(hsh)
166
332
  str_array = [@md.code(hsh[:name]), param_type_expr(hsh, rparam), param_default_value(hsh, rparam)]
333
+ desc = param_description(hsh)
334
+ str_array << desc if desc
167
335
  @md.add_ul(str_array.compact.join, indent: 1)
168
336
  end
169
337
  end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validation_finding'
4
+
5
+ module AbideDevUtils
6
+ module CEM
7
+ module Validate
8
+ module Strings
9
+ # Base class for validating Puppet Strings objects. This class can be used directly, but it is
10
+ # recommended to use a subclass of this class to provide more specific validation logic. Each
11
+ # subclass should implement a `validate_<type>` method that will be called by the `validate` method
12
+ # of this class. The `validate_<type>` method should contain the validation logic for the
13
+ # corresponding type of Puppet Strings object.
14
+ class BaseValidator
15
+ SAFE_OBJECT_METHODS = %i[
16
+ type
17
+ name
18
+ file
19
+ line
20
+ docstring
21
+ tags
22
+ parameters
23
+ source
24
+ ].freeze
25
+ PDK_SUMMARY_REGEX = %r{^A short summary of the purpose.*}.freeze
26
+ PDK_DESCRIPTION_REGEX = %r{^A description of what this.*}.freeze
27
+
28
+ attr_reader :findings
29
+
30
+ def initialize(strings_object)
31
+ @object = strings_object
32
+ @findings = []
33
+ # Define instance methods for each of the SAFE_OBJECT_METHODS
34
+ SAFE_OBJECT_METHODS.each do |method|
35
+ define_singleton_method(method) { safe_method_call(@object, method) }
36
+ end
37
+ end
38
+
39
+ def errors
40
+ @findings.select { |f| f.type == :error }
41
+ end
42
+
43
+ def warnings
44
+ @findings.select { |f| f.type == :warning }
45
+ end
46
+
47
+ def errors?
48
+ !errors.empty?
49
+ end
50
+
51
+ def warnings?
52
+ !warnings.empty?
53
+ end
54
+
55
+ def find_tag_name(tag_name)
56
+ tags&.find { |t| t.tag_name == tag_name }
57
+ end
58
+
59
+ def select_tag_name(tag_name)
60
+ return [] if tags.nil?
61
+
62
+ tags.select { |t| t.tag_name == tag_name }
63
+ end
64
+
65
+ def find_parameter(param_name)
66
+ parameters&.find { |p| p[0] == param_name }
67
+ end
68
+
69
+ def validate
70
+ license_tag?
71
+ non_generic_summary?
72
+ non_generic_description?
73
+ send("validate_#{type}".to_sym) if respond_to?("validate_#{type}".to_sym)
74
+ end
75
+
76
+ # Checks if the object has a license tag and if it is formatted correctly.
77
+ # Comparison is not case sensitive.
78
+ def license_tag?
79
+ see_tags = select_tag_name('see')
80
+ if see_tags.empty? || see_tags.none? { |t| t.name.casecmp('LICENSE.pdf') && t.text.casecmp('for license') }
81
+ new_finding(
82
+ :error,
83
+ :no_license_tag,
84
+ remediation: 'Add "@see LICENSE.pdf for license" to the class documentation'
85
+ )
86
+ return false
87
+ end
88
+ true
89
+ end
90
+
91
+ # Checks if the summary is not the default PDK summary.
92
+ def non_generic_summary?
93
+ summary = find_tag_name('summary')&.text
94
+ return true if summary.nil?
95
+
96
+ if summary.match?(PDK_SUMMARY_REGEX)
97
+ new_finding(:warning, :generic_summary, remediation: 'Add a more descriptive summary')
98
+ return false
99
+ end
100
+ true
101
+ end
102
+
103
+ # Checks if the description is not the default PDK description.
104
+ def non_generic_description?
105
+ description = docstring
106
+ return true if description.nil?
107
+
108
+ if description.match?(PDK_DESCRIPTION_REGEX)
109
+ new_finding(:warning, :generic_description, remediation: 'Add a more descriptive description')
110
+ return false
111
+ end
112
+ true
113
+ end
114
+
115
+ private
116
+
117
+ def safe_method_call(obj, method, *args)
118
+ obj.send(method, *args)
119
+ rescue NoMethodError
120
+ nil
121
+ end
122
+
123
+ def new_finding(type, title, **data)
124
+ @findings << ValidationFinding.new(type, title.to_sym, data)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end