diff --git a/lib/puppet-lint.rb b/lib/puppet-lint.rb index fbe539444..91ce2c79e 100644 --- a/lib/puppet-lint.rb +++ b/lib/puppet-lint.rb @@ -161,7 +161,7 @@ def print_context(message) # problems - An Array of problem Hashes as returned by # PuppetLint::Checks#run. # - # Returns nothing. + # Returns array of problem. def report(problems) json = [] problems.each do |message| @@ -171,7 +171,7 @@ def report(problems) next unless message[:kind] == :fixed || [message[:kind], :all].include?(configuration.error_level) - if configuration.json + if configuration.json || configuration.sarif message['context'] = get_context(message) if configuration.with_context json << message else @@ -180,9 +180,8 @@ def report(problems) print_github_annotation(message) if configuration.github_actions end end - puts JSON.pretty_generate(json) if configuration.json - $stderr.puts 'Try running `puppet parser validate `' if problems.any? { |p| p[:check] == :syntax } + json end # Public: Determine if PuppetLint found any errors in the manifest. diff --git a/lib/puppet-lint/bin.rb b/lib/puppet-lint/bin.rb index 9ff13bf43..0466cac90 100644 --- a/lib/puppet-lint/bin.rb +++ b/lib/puppet-lint/bin.rb @@ -1,3 +1,5 @@ +require 'pathname' +require 'uri' require 'puppet-lint/optparser' # Internal: The logic of the puppet-lint bin script, contained in a class for @@ -46,6 +48,21 @@ def run begin path = @args[0] + full_path = File.expand_path(path, ENV['PWD']) + full_base_path = if File.directory?(full_path) + full_path + else + File.dirname(full_path) + end + + full_base_path_uri = if full_base_path.start_with?('/') + 'file://' + full_base_path + else + 'file:///' + full_base_path + end + + full_base_path_uri += '/' unless full_base_path_uri.end_with?('/') + path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR path = if File.directory?(path) Dir.glob("#{path}/**/*.pp") @@ -57,15 +74,14 @@ def run return_val = 0 ignore_paths = PuppetLint.configuration.ignore_paths + all_problems = [] - puts '[' if PuppetLint.configuration.json path.each do |f| next if ignore_paths.any? { |p| File.fnmatch(p, f) } l = PuppetLint.new l.file = f l.run - l.print_problems - puts ',' if f != path.last && PuppetLint.configuration.json + all_problems << l.print_problems if l.errors? || (l.warnings? && PuppetLint.configuration.fail_on_warnings) return_val = 1 @@ -76,7 +92,17 @@ def run fd.write(l.manifest) end end - puts ']' if PuppetLint.configuration.json + + if PuppetLint.configuration.sarif + report_sarif(all_problems, full_base_path, full_base_path_uri) + elsif PuppetLint.configuration.json + all_problems.each do |problems| + problems.each do |problem| + problem.delete_if { |key, _| [:description, :help_uri].include?(key) } + end + end + puts JSON.pretty_generate(all_problems) + end return return_val rescue PuppetLint::NoCodeError @@ -85,4 +111,42 @@ def run return 1 end end + + # Internal: Print the reported problems in SARIF format to stdout. + # + # problems - An Array of problem Hashes as returned by + # PuppetLint::Checks#run. + # + # Returns nothing. + def report_sarif(problems, base_path, base_path_uri) + sarif_file = File.read(File.join(__dir__, 'report', 'sarif_template.json')) + sarif = JSON.parse(sarif_file) + sarif['runs'][0]['originalUriBaseIds']['ROOTPATH']['uri'] = base_path_uri + rules = sarif['runs'][0]['tool']['driver']['rules'] = [] + results = sarif['runs'][0]['results'] = [] + problems.each do |messages| + messages.each do |message| + relative_path = Pathname.new(message[:fullpath]).relative_path_from(Pathname.new(base_path)) + rules = sarif['runs'][0]['tool']['driver']['rules'] + rule_exists = rules.any? { |r| r['id'] == message[:check] } + unless rule_exists + new_rule = { 'id' => message[:check] } + new_rule['fullDescription'] = { 'text' => message[:description] } unless message[:description].nil? + new_rule['fullDescription'] = { 'text' => 'Check for any syntax error and record an error of each instance found.' } if new_rule['fullDescription'].nil? && message[:check] == :syntax + new_rule['defaultConfiguration'] = { 'level' => message[:KIND].downcase } if %w[error warning].include?(message[:KIND].downcase) + new_rule['helpUri'] = message[:help_uri] unless message[:help_uri].nil? + rules << new_rule + end + rule_index = rules.index { |r| r['id'] == message[:check] } + result = { + 'ruleId' => message[:check], + 'ruleIndex' => rule_index, + 'message' => { 'text' => message[:message] }, + 'locations' => [{ 'physicalLocation' => { 'artifactLocation' => { 'uri' => relative_path, 'uriBaseId' => 'ROOTPATH' }, 'region' => { 'startLine' => message[:line], 'startColumn' => message[:column] } } }], + } + results << result + end + end + puts JSON.pretty_generate(sarif) + end end diff --git a/lib/puppet-lint/checkplugin.rb b/lib/puppet-lint/checkplugin.rb index a1fa65730..65f84de56 100644 --- a/lib/puppet-lint/checkplugin.rb +++ b/lib/puppet-lint/checkplugin.rb @@ -158,10 +158,12 @@ def default_info # # kind - The Symbol problem type (:warning or :error). # problem - A Hash containing the attributes of the problem - # :message - The String message describing the problem. - # :line - The Integer line number of the location of the problem. - # :column - The Integer column number of the location of the problem. - # :check - The Symbol name of the check that detected the problem. + # :message - The String message describing the problem. + # :line - The Integer line number of the location of the problem. + # :column - The Integer column number of the location of the problem. + # :check - The Symbol name of the check that detected the problem. + # :description - The description of the check that detected the problem. + # :help_uri - The help web page of the check that detected the problem. # # Returns nothing. def notify(kind, problem) diff --git a/lib/puppet-lint/configuration.rb b/lib/puppet-lint/configuration.rb index 5dc8b7a2f..ebf9c3735 100644 --- a/lib/puppet-lint/configuration.rb +++ b/lib/puppet-lint/configuration.rb @@ -150,6 +150,7 @@ def defaults self.with_context = false self.fix = false self.json = false + self.sarif = false self.show_ignored = false self.ignore_paths = ['vendor/**/*.pp'] self.github_actions = ENV.key?('GITHUB_ACTION') diff --git a/lib/puppet-lint/data.rb b/lib/puppet-lint/data.rb index 652408980..4a5c946a2 100644 --- a/lib/puppet-lint/data.rb +++ b/lib/puppet-lint/data.rb @@ -591,7 +591,7 @@ def parse_control_comments if top_override.nil? # TODO: refactor to provide a way to expose problems from # PuppetLint::Data via the normal problem reporting mechanism. - puts "WARNING: lint:endignore comment with no opening lint:ignore: comment found on line #{token.line}" + $stderr.puts "WARNING: lint:endignore comment with no opening lint:ignore: comment found on line #{token.line}" else top_override.each do |start| next if start.nil? @@ -607,7 +607,7 @@ def parse_control_comments end stack.each do |control| - puts "WARNING: lint:ignore:#{control[0][2]} comment on line #{control[0][0]} with no closing lint:endignore comment" + $stderr.puts "WARNING: lint:ignore:#{control[0][2]} comment on line #{control[0][0]} with no closing lint:endignore comment" end end end diff --git a/lib/puppet-lint/optparser.rb b/lib/puppet-lint/optparser.rb index bb1e1ef1b..677d01751 100644 --- a/lib/puppet-lint/optparser.rb +++ b/lib/puppet-lint/optparser.rb @@ -98,6 +98,10 @@ def self.build(args = []) PuppetLint.configuration.json = true end + opts.on('--sarif', 'Log output as SARIF') do + PuppetLint.configuration.sarif = true + end + opts.on('--list-checks', 'List available check names.') do PuppetLint.configuration.list_checks = true end diff --git a/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb b/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb index d2646f45f..633f97b67 100644 --- a/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb +++ b/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb @@ -9,10 +9,12 @@ def check notify( :warning, - :message => "arrow should be on the right operand's line", - :line => token.line, - :column => token.column, - :token => token + :message => "arrow should be on the right operand's line", + :line => token.line, + :column => token.column, + :token => token, + :description => 'Test the manifest tokens for chaining arrow that is on the line of the left operand when the right operand is on another line.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#chaining-arrow-syntax' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb b/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb index 2cd99b066..87efb239c 100644 --- a/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb +++ b/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb @@ -25,9 +25,11 @@ def check notify( :error, - :message => "#{title_token.value} not in autoload module layout", - :line => title_token.line, - :column => title_token.column + :message => "#{title_token.value} not in autoload module layout", + :line => title_token.line, + :column => title_token.column, + :description => 'Test the manifest tokens for any classes or defined types that are not in an appropriately named file for the autoloader to detect and record an error of each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#separate-files' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb b/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb index cadf636f2..ff04ccfb3 100644 --- a/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb +++ b/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb @@ -9,9 +9,11 @@ def check notify( :warning, - :message => 'class inheriting from params class', - :line => class_idx[:inherited_token].line, - :column => class_idx[:inherited_token].column + :message => 'class inheriting from params class', + :line => class_idx[:inherited_token].line, + :column => class_idx[:inherited_token].column, + :description => 'Check the manifest tokens for any classes that inherit a params subclass and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb b/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb index cc9a6089a..014dceafb 100644 --- a/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb +++ b/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb @@ -11,9 +11,11 @@ def check notify( :warning, - :message => "code outside of class or define block - #{token.value}", - :line => token.line, - :column => token.column + :message => "code outside of class or define block - #{token.value}", + :line => token.line, + :column => token.column, + :description => 'Test that no code is outside of a class or define scope.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb b/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb index 61d849a3b..ec09758bc 100644 --- a/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb +++ b/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb @@ -14,9 +14,11 @@ def check notify( :warning, - :message => 'class inherits across module namespaces', - :line => class_idx[:inherited_token].line, - :column => class_idx[:inherited_token].column + :message => 'class inherits across module namespaces', + :line => class_idx[:inherited_token].line, + :column => class_idx[:inherited_token].column, + :description => 'Test the manifest tokens for any classes that inherit across namespaces and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#class-inheritance' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb b/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb index 08c0fd76d..32bf1899a 100644 --- a/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb +++ b/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb @@ -15,9 +15,11 @@ def check notify( :error, - :message => "#{obj_type} name containing a dash", - :line => class_idx[:name_token].line, - :column => class_idx[:name_token].column + :message => "#{obj_type} name containing a dash", + :line => class_idx[:name_token].line, + :column => class_idx[:name_token].column, + :description => 'Check the manifest tokens for any classes or defined types that have a dash in their name and record an error for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb b/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb index c4e53d81a..f7cf3e850 100644 --- a/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb +++ b/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb @@ -15,10 +15,12 @@ def check notify( :error, - :message => "#{obj_type} '#{class_idx[:name_token].value}' contains illegal uppercase", - :line => class_idx[:name_token].line, - :column => class_idx[:name_token].column, - :token => class_idx[:name_token] + :message => "#{obj_type} '#{class_idx[:name_token].value}' contains illegal uppercase", + :line => class_idx[:name_token].line, + :column => class_idx[:name_token].column, + :token => class_idx[:name_token], + :description => 'Find and warn about module names with illegal uppercase characters.', + :help_uri => 'https://puppet.com/docs/puppet/latest/modules_fundamentals.html#allowed-module-names' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb b/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb index b86b8b123..e59ae5b33 100644 --- a/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb +++ b/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb @@ -17,9 +17,11 @@ def check notify( :warning, - :message => "#{type} defined inside a class", - :line => token.line, - :column => token.column + :message => "#{type} defined inside a class", + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for any classes or defined types that are defined inside another class.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#nested-classes-or-defined-types' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/parameter_order.rb b/lib/puppet-lint/plugins/check_classes/parameter_order.rb index ddadd80e0..3386d9e23 100644 --- a/lib/puppet-lint/plugins/check_classes/parameter_order.rb +++ b/lib/puppet-lint/plugins/check_classes/parameter_order.rb @@ -31,9 +31,11 @@ def check msg = 'optional parameter listed before required parameter' notify( :warning, - :message => msg, - :line => token.line, - :column => token.column + :message => msg, + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for any parameterised classes or defined types that take parameters and record a warning if there are any optional parameters listed before required parameters.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#display-order-of-parameters' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb b/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb index 926a60c86..11d04a22c 100644 --- a/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb +++ b/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb @@ -7,9 +7,11 @@ def check tokens.select { |r| r.type == :OUT_EDGE }.each do |token| notify( :warning, - :message => 'right-to-left (<-) relationship', - :line => token.line, - :column => token.column + :message => 'right-to-left (<-) relationship', + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for any right-to-left (<-) chaining operators and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#chaining-arrow-syntax' ) end end diff --git a/lib/puppet-lint/plugins/check_classes/variable_scope.rb b/lib/puppet-lint/plugins/check_classes/variable_scope.rb index d6960b542..d17b0d7bd 100644 --- a/lib/puppet-lint/plugins/check_classes/variable_scope.rb +++ b/lib/puppet-lint/plugins/check_classes/variable_scope.rb @@ -130,9 +130,13 @@ def check notify( :warning, - :message => msg, - :line => token.line, - :column => token.column + :message => msg, + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for any variables that are referenced in the manifest. ' \ + 'If the variables are not fully qualified or one of the variables automatically created in the scope, ' \ + 'check that they have been defined in the local scope and record a warning for each variable that has not.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#namespacing-variables' ) end end diff --git a/lib/puppet-lint/plugins/check_comments/slash_comments.rb b/lib/puppet-lint/plugins/check_comments/slash_comments.rb index 2e6280e1a..a9c3a8fd6 100644 --- a/lib/puppet-lint/plugins/check_comments/slash_comments.rb +++ b/lib/puppet-lint/plugins/check_comments/slash_comments.rb @@ -9,10 +9,12 @@ def check }.each do |token| notify( :warning, - :message => '// comment found', - :line => token.line, - :column => token.column, - :token => token + :message => '// comment found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any comments started with slashes (//) and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#comments' ) end end diff --git a/lib/puppet-lint/plugins/check_comments/star_comments.rb b/lib/puppet-lint/plugins/check_comments/star_comments.rb index e42e0ea8c..268bc5845 100644 --- a/lib/puppet-lint/plugins/check_comments/star_comments.rb +++ b/lib/puppet-lint/plugins/check_comments/star_comments.rb @@ -9,10 +9,12 @@ def check }.each do |token| notify( :warning, - :message => '/* */ comment found', - :line => token.line, - :column => token.column, - :token => token + :message => '/* */ comment found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any comments encapsulated with slash-asterisks (/* */) and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#comments' ) end end diff --git a/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb b/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb index 72f257552..fe06392c8 100644 --- a/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb +++ b/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb @@ -49,9 +49,11 @@ def check notify( :warning, - :message => 'case statement without a default case', - :line => case_tokens.first.line, - :column => case_tokens.first.column + :message => 'case statement without a default case', + :line => case_tokens.first.line, + :column => case_tokens.first.column, + :description => 'Test the manifest tokens for any case statements that do not contain a "default" case and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#defaults-for-case-statements-and-selectors' ) end end diff --git a/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb b/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb index 196ad689f..241e7db3f 100644 --- a/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb +++ b/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb @@ -12,9 +12,11 @@ def check notify( :warning, - :message => 'selector inside resource block', - :line => token.line, - :column => token.column + :message => 'selector inside resource block', + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for any selectors embedded within resource declarations and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#keep-resource-declarations-simple' ) end end diff --git a/lib/puppet-lint/plugins/check_documentation/documentation.rb b/lib/puppet-lint/plugins/check_documentation/documentation.rb index 66564c276..419427163 100644 --- a/lib/puppet-lint/plugins/check_documentation/documentation.rb +++ b/lib/puppet-lint/plugins/check_documentation/documentation.rb @@ -22,9 +22,11 @@ def check notify( :warning, - :message => "#{type} not documented", - :line => first_token.line, - :column => first_token.column + :message => "#{type} not documented", + :line => first_token.line, + :column => first_token.column, + :description => 'Check the manifest tokens for any class or defined type that does not have a comment directly above it (hopefully, explaining the usage of it) and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#public-and-private' ) end end diff --git a/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb b/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb index 50a9f1411..41cf23cce 100644 --- a/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb +++ b/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb @@ -11,10 +11,12 @@ def check if node_lbrace_tok.nil? notify( :error, - :check => :syntax, - :message => 'Syntax error (try running `puppet parser validate `)', - :line => node.line, - :column => node.column + :check => :syntax, + :message => 'Syntax error (try running `puppet parser validate `)', + :line => node.line, + :column => node.column, + :description => 'Check for any syntax error and record an error of each instance found.', + :help_uri => nil ) next end @@ -26,10 +28,12 @@ def check }.each do |token| notify( :warning, - :message => 'unquoted node name found', - :line => token.line, - :column => token.column, - :token => token + :message => 'unquoted node name found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest for unquoted node names and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_resources/duplicate_params.rb b/lib/puppet-lint/plugins/check_resources/duplicate_params.rb index ac46e3144..13be04a99 100644 --- a/lib/puppet-lint/plugins/check_resources/duplicate_params.rb +++ b/lib/puppet-lint/plugins/check_resources/duplicate_params.rb @@ -24,9 +24,11 @@ def check if (seen_params[level] ||= Set.new).include?(prev_token.value) notify( :error, - :message => 'duplicate parameter found in resource', - :line => prev_token.line, - :column => prev_token.column + :message => 'duplicate parameter found in resource', + :line => prev_token.line, + :column => prev_token.column, + :description => 'Check the tokens of each resource instance for any duplicate parameters and record a warning for each instance found.', + :help_uri => nil ) else seen_params[level] << prev_token.value diff --git a/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb b/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb index 019913b39..79bce3c31 100644 --- a/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +++ b/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb @@ -17,10 +17,12 @@ def check ensure_token = resource[:param_tokens][ensure_attr_index] notify( :warning, - :message => "ensure found on line but it's not the first attribute", - :line => ensure_token.line, - :column => ensure_token.column, - :resource => resource + :message => "ensure found on line but it's not the first attribute", + :line => ensure_token.line, + :column => ensure_token.column, + :resource => resource, + :description => 'Check the tokens of each resource instance for an ensure parameter and if found, check that it is the first parameter listed. If it is not the first parameter, record a warning.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#attribute-ordering' ) end end diff --git a/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb b/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb index d42b39993..2cb355c24 100644 --- a/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb +++ b/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb @@ -20,7 +20,9 @@ def check :line => value_token.line, :column => value_token.column, :param_token => ensure_token, - :value_token => value_token + :value_token => value_token, + :description => 'Check the tokens of each File resource instance for an ensure parameter and record a warning if the value of that parameter looks like a symlink target (starts with a \'/\').', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#symbolic-links' ) end end diff --git a/lib/puppet-lint/plugins/check_resources/file_mode.rb b/lib/puppet-lint/plugins/check_resources/file_mode.rb index 20417cb9b..af5e83f9c 100644 --- a/lib/puppet-lint/plugins/check_resources/file_mode.rb +++ b/lib/puppet-lint/plugins/check_resources/file_mode.rb @@ -23,10 +23,13 @@ def check notify( :warning, - :message => MSG, - :line => value_token.line, - :column => value_token.column, - :token => value_token + :message => MSG, + :line => value_token.line, + :column => value_token.column, + :token => value_token, + :description => 'Check the tokens of each File resource instance for a mode parameter and if found, ' \ + 'record a warning if the value of that parameter is not a 4 digit octal value (0755) or a symbolic mode (\'o=rwx,g\+r\').', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#file-modes' ) end end diff --git a/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb b/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb index 285804db0..cb66eb2f5 100644 --- a/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb +++ b/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb @@ -17,10 +17,12 @@ def check value_token = param_token.next_code_token.next_code_token notify( :warning, - :message => 'unquoted file mode', - :line => value_token.line, - :column => value_token.column, - :token => value_token + :message => 'unquoted file mode', + :line => value_token.line, + :column => value_token.column, + :token => value_token, + :description => 'Check the tokens of each File resource instance for a mode parameter and if found, record a warning if the value of that parameter is not a quoted string.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#file-modes' ) end end diff --git a/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb b/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb index 0b3294cf3..a78cebde5 100644 --- a/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb +++ b/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb @@ -9,10 +9,12 @@ def check notify( :warning, - :message => 'unquoted resource title', - :line => token.line, - :column => token.column, - :token => token + :message => 'unquoted resource title', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any resource titles / namevars that are not quoted and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#resource-names' ) end end diff --git a/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb b/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb index 74388154f..3bd61e703 100644 --- a/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb +++ b/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb @@ -13,10 +13,12 @@ def check }.each do |token| notify( :warning, - :message => 'double quoted string containing no variables', - :line => token.line, - :column => token.column, - :token => token + :message => 'double quoted string containing no variables', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any double quoted strings that don\'t contain any variables or common escape characters and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting' ) end end diff --git a/lib/puppet-lint/plugins/check_strings/only_variable_string.rb b/lib/puppet-lint/plugins/check_strings/only_variable_string.rb index ea44b0d91..3e13d259c 100644 --- a/lib/puppet-lint/plugins/check_strings/only_variable_string.rb +++ b/lib/puppet-lint/plugins/check_strings/only_variable_string.rb @@ -30,7 +30,9 @@ def check :column => var_token.column, :start_token => start_token, :var_token => var_token, - :end_token => eos_token + :end_token => eos_token, + :description => 'Check the manifest tokens for double quoted strings that contain a single variable only and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting' ) end break diff --git a/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb b/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb index 7783682f9..47513b21e 100644 --- a/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb +++ b/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb @@ -12,10 +12,12 @@ def check }.each do |token| notify( :warning, - :message => 'puppet:// URL without modules/ found', - :line => token.line, - :column => token.column, - :token => token + :message => 'puppet:// URL without modules/ found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any puppet:// URL strings where the path section doesn\'t start with modules/ and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb b/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb index f031ea05c..fe491ef78 100644 --- a/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +++ b/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb @@ -13,10 +13,12 @@ def check }.each do |token| notify( :warning, - :message => 'quoted boolean value found', - :line => token.line, - :column => token.column, - :token => token + :message => 'quoted boolean value found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any double or single quoted strings containing only a boolean value and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb b/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb index 6a18adcd1..8f53ab7e7 100644 --- a/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb +++ b/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb @@ -9,9 +9,11 @@ def check }.each do |token| notify( :error, - :message => 'single quoted string containing a variable found', - :line => token.line, - :column => token.column + :message => 'single quoted string containing a variable found', + :line => token.line, + :column => token.column, + :description => 'Check the manifest tokens for any single quoted strings containing a enclosed variable and record an error for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting' ) end end diff --git a/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb b/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb index f215e7363..b17bd6cd9 100644 --- a/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +++ b/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb @@ -20,10 +20,12 @@ def check }.each do |token| notify( :warning, - :message => 'variable not enclosed in {}', - :line => token.line, - :column => token.column, - :token => token + :message => 'variable not enclosed in {}', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for any variables in a string that have not been enclosed by braces ({}) and record a warning for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#quoting' ) end end diff --git a/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb b/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb index 5e65e6e4a..e89024322 100644 --- a/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb +++ b/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb @@ -13,9 +13,11 @@ def check notify( :warning, - :message => 'variable contains a dash', - :line => token.line, - :column => token.column + :message => 'variable contains a dash', + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for variables that contain a dash and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_variables/variable_is_lowercase.rb b/lib/puppet-lint/plugins/check_variables/variable_is_lowercase.rb index 6d90c0f1c..d13e57b91 100644 --- a/lib/puppet-lint/plugins/check_variables/variable_is_lowercase.rb +++ b/lib/puppet-lint/plugins/check_variables/variable_is_lowercase.rb @@ -13,9 +13,11 @@ def check notify( :warning, - :message => 'variable contains an uppercase letter', - :line => token.line, - :column => token.column + :message => 'variable contains an uppercase letter', + :line => token.line, + :column => token.column, + :description => 'Test the manifest tokens for variables that contain an uppercase letter and record a warning for each instance found.', + :help_uri => nil ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/140chars.rb b/lib/puppet-lint/plugins/check_whitespace/140chars.rb index be3f18cde..780e976de 100644 --- a/lib/puppet-lint/plugins/check_whitespace/140chars.rb +++ b/lib/puppet-lint/plugins/check_whitespace/140chars.rb @@ -12,9 +12,12 @@ def check notify( :warning, - :message => 'line has more than 140 characters', - :line => idx + 1, - :column => 140 + :message => 'line has more than 140 characters', + :line => idx + 1, + :column => 140, + :description => 'Test the raw manifest string for lines containing more than 140 characters and record a warning for each instance found. ' \ + 'The only exceptions to this rule are lines containing URLs and template() calls which would hurt readability if split.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb b/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb index 592cb217a..87ab48fd3 100644 --- a/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb +++ b/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb @@ -11,9 +11,11 @@ def check }.each do |token| notify( :error, - :message => 'two-space soft tabs not used', - :line => token.line, - :column => token.column + :message => 'two-space soft tabs not used', + :line => token.line, + :column => token.column, + :description => 'Check the manifest tokens for any indentation not using 2 space soft tabs and record an error for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/80chars.rb b/lib/puppet-lint/plugins/check_whitespace/80chars.rb index dafc577b0..559aa7018 100644 --- a/lib/puppet-lint/plugins/check_whitespace/80chars.rb +++ b/lib/puppet-lint/plugins/check_whitespace/80chars.rb @@ -11,9 +11,11 @@ def check notify( :warning, - :message => 'line has more than 80 characters', - :line => idx + 1, - :column => 80 + :message => 'line has more than 80 characters', + :line => idx + 1, + :column => 80, + :description => 'Test the raw manifest string for lines containing more than 80 characters. This is DISABLED by default and behaves like the default 140chars check by excepting URLs and template() calls.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb b/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb index d687179b8..e80470558 100644 --- a/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb +++ b/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb @@ -78,7 +78,9 @@ def check :token => arrow_tok, :arrow_column => arrow_column[level_idx], :newline => arrows_on_line.index(arrow_tok) != 0, - :newline_indent => param_column[level_idx] - 1 + :newline_indent => param_column[level_idx] - 1, + :description => 'Check the manifest tokens for any arrows (=>) in a grouping ({}) that are not aligned with other arrows in that grouping.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb b/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb index 449b8f46d..e82c7e1a7 100644 --- a/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb +++ b/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb @@ -11,10 +11,12 @@ def check }.each do |token| notify( :error, - :message => 'tab character found', - :line => token.line, - :column => token.column, - :token => token + :message => 'tab character found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the raw manifest string for lines containing hard tab characters and record an error for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb b/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb index b19996e10..cbd526ad0 100644 --- a/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb +++ b/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb @@ -11,10 +11,12 @@ def check }.each do |token| notify( :error, - :message => 'trailing whitespace found', - :line => token.line, - :column => token.column, - :token => token + :message => 'trailing whitespace found', + :line => token.line, + :column => token.column, + :token => token, + :description => 'Check the manifest tokens for lines ending with whitespace and record an error for each instance found.', + :help_uri => 'https://puppet.com/docs/puppet/latest/style_guide.html#spacing-indentation-and-whitespace' ) end end diff --git a/lib/puppet-lint/report/sarif_template.json b/lib/puppet-lint/report/sarif_template.json new file mode 100644 index 000000000..d3d33260c --- /dev/null +++ b/lib/puppet-lint/report/sarif_template.json @@ -0,0 +1,63 @@ +{ + "$schema":"https://www.schemastore.org/schemas/json/sarif-2.1.0-rtm.5.json", + "version":"2.1.0", + "runs":[ + { + "tool":{ + "driver":{ + "name":"Puppet Lint", + "informationUri":"https://github.com/puppetlabs/puppet-lint", + "version":"0.15.0", + "rules":[ + { + "id":"", + "name":"", + "shortDescription":{ + "text":"" + }, + "fullDescription":{ + "text":"" + }, + "defaultConfiguration":{ + "level":"warning" + }, + "help":{ + "text":"" + } + } + ] + } + }, + "results":[ + { + "ruleId":"", + "ruleIndex":0, + "level":"error", + "message":{ + "text":"" + }, + "locations":[ + { + "physicalLocation":{ + "artifactLocation":{ + "uri":"puppet-lint/spec/fixtures/test/manifests/fail.pp", + "uriBaseId":"ROOTPATH" + }, + "region":{ + "startLine":1, + "startColumn":1 + } + } + } + ] + } + ], + "columnKind":"utf16CodeUnits", + "originalUriBaseIds":{ + "ROOTPATH":{ + "uri":"file:///home/user/repos/" + } + } + } + ] +} \ No newline at end of file diff --git a/spec/puppet-lint/bin_spec.rb b/spec/puppet-lint/bin_spec.rb index e031d539e..d60bceb78 100644 --- a/spec/puppet-lint/bin_spec.rb +++ b/spec/puppet-lint/bin_spec.rb @@ -397,6 +397,41 @@ def initialize(args) end end + context 'when displaying results as SARIF' do + let(:args) do + [ + '--sarif', + 'spec/fixtures/test/manifests/warning.pp', + ] + end + + its(:exitstatus) { is_expected.to eq(0) } + + its(:stdout) do + is_expected.to match(%r{"ruleId": "parameter_order"}) + is_expected.to match(%r{"uri": "warning.pp"}) + end + end + + context 'when displaying results for multiple targets as SARIF' do + let(:args) do + [ + '--sarif', + 'spec/fixtures/test/manifests/fail.pp', + 'spec/fixtures/test/manifests/warning.pp', + ] + end + + its(:exitstatus) { is_expected.to eq(1) } + + its(:stdout) do + is_expected.to match(%r{"ruleId": "autoloader_layout"}) + is_expected.to match(%r{"uri": "fail.pp"}) + is_expected.to match(%r{"ruleId": "parameter_order"}) + is_expected.to match(%r{"uri": "warning.pp"}) + end + end + context 'when hiding ignored problems' do let(:args) do [ @@ -468,7 +503,7 @@ def initialize(args) end its(:exitstatus) { is_expected.to eq(0) } - its(:stdout) { is_expected.to match(%r{WARNING: lint:endignore comment with no opening lint:ignore: comment found on line 1}) } + its(:stderr) { is_expected.to match(%r{WARNING: lint:endignore comment with no opening lint:ignore: comment found on line 1}) } end context 'when a lint:ignore control comment block is not terminated properly' do @@ -478,7 +513,7 @@ def initialize(args) ] end - its(:stdout) { is_expected.to match(%r{WARNING: lint:ignore:140chars comment on line 2 with no closing lint:endignore comment}) } + its(:stderr) { is_expected.to match(%r{WARNING: lint:ignore:140chars comment on line 2 with no closing lint:endignore comment}) } end context 'when fixing a file with \n line endings' do diff --git a/spec/puppet-lint/configuration_spec.rb b/spec/puppet-lint/configuration_spec.rb index cda6c0cec..f412b048c 100644 --- a/spec/puppet-lint/configuration_spec.rb +++ b/spec/puppet-lint/configuration_spec.rb @@ -59,6 +59,7 @@ 'fail_on_warnings' => false, 'error_level' => :all, 'log_format' => '', + 'sarif' => false, 'with_context' => false, 'fix' => false, 'github_actions' => false,