abide_dev_utils 0.13.0 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
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