-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SARIF support #40
Changes from 7 commits
60e3ac2
283382b
8627248
8d15b84
b2e0dce
40cfdd4
d916b98
db93036
6e42257
f9555df
40c367a
99c91a3
8af4705
427333f
369fadd
36e1fbd
747505a
d145c7f
0123103
ef1b694
16d625d
7fcf324
1b2da02
01f8bd8
17ad7a8
119f16f
57c7af8
7232414
74b39e6
9d91452
eca7fe0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: puppet-lint scan | ||
|
||
on: | ||
# Allows you to run this workflow manually from the Actions tab | ||
workflow_dispatch: | ||
|
||
jobs: | ||
|
||
security: | ||
|
||
strategy: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels odd to have a matrix here if there's just a single entry. Is that really needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Matrix is not needed, I have removed it. |
||
matrix: | ||
operating-system: [ubuntu-latest] | ||
|
||
runs-on: ${{ matrix.operating-system }} | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
|
||
- name: Setup Ruby | ||
uses: ruby/setup-ruby@v1 | ||
with: | ||
ruby-version: 2.7 | ||
bundler-cache: true | ||
|
||
- name: Scan | ||
continue-on-error: true | ||
run: bin/puppet-lint . --sarif > results.sarif | ||
|
||
- name: Upload artifact | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
path: results.sarif | ||
|
||
- name: Upload SARIF to GitHub | ||
uses: github/codeql-action/upload-sarif@v1 | ||
with: | ||
sarif_file: results.sarif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could affect usage via Rake. I don't know if people do this, but there are effectively 2 entry points: the bin file which you already found and Rake: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for all the inputs! I will set the PR as draft for now and look into them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have followed this instruction: then running |
||
|
||
$stderr.puts 'Try running `puppet parser validate <file>`' if problems.any? { |p| p[:check] == :syntax } | ||
json | ||
end | ||
|
||
# Public: Determine if PuppetLint found any errors in the manifest. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure the description must be excluded. The help URI also doesn't hurt IMHO. Perhaps a maintainer can weigh in on that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps a maintainer can weigh in on that. ---- yes the only reason for this code block is I am trying to maintain the same JSON output as before, maintainer can advise if also want these 2 new properties in JSON and let me know to change accordingly. |
||
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.count - 1 | ||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting thing. Do you have an example of where this ran so I can see the resulting report?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a sample (or sometimes called a starter workflow) that the user of the puppet-lint can add to their own repo, after we got all things done. User can use it to scan their repo and get code scanning alerts of the .pp files in their repo.
GitHub support this and the format used is SARIF format.
this is a run in my fork as example: (it is not intended to run against the puppet-lint repo itself, but the user's repo)
https://github.com/shaopeng-gh/puppet-lint/security/code-scanning
for people don't have permission: