Compare commits
15 Commits
master
...
coverage-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec568a8682 | ||
|
|
829bd10190 | ||
|
|
21926a5e7d | ||
|
|
78e98633e0 | ||
|
|
7436a56daa | ||
|
|
96f5618266 | ||
|
|
25b58a6a5d | ||
|
|
1c13cdc80e | ||
|
|
22a9598b0f | ||
|
|
10ef5432cc | ||
|
|
6fe95f9b39 | ||
|
|
eb7a4513da | ||
|
|
e86bde2331 | ||
|
|
9247c263a3 | ||
|
|
d7c0892c88 |
@ -8,4 +8,4 @@ Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 120
|
||||
Max: 150
|
||||
|
||||
33
README.md
33
README.md
@ -1,34 +1,21 @@
|
||||
## Brakeman github action
|
||||
|
||||
Brakeman is a static analysis tool which checks Ruby on Rails applications for security vulnerabilities.
|
||||
[See more](https://github.com/presidentbeef/brakeman)
|
||||
Check your coverage percentage.
|
||||
|
||||
### Usage
|
||||
|
||||
```yml
|
||||
- name: Brakeman
|
||||
uses: devmasx/brakeman-linter-action@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
```
|
||||
|
||||
### Custom report
|
||||
#### [Simplecov](https://github.com/colszowka/simplecov)
|
||||
|
||||
```yml
|
||||
- name: Install gems
|
||||
run: |
|
||||
gem install brakeman -v 4.5.0
|
||||
- name: brakeman report
|
||||
run: |
|
||||
brakeman -f json > tmp/brakeman.json || exit 0
|
||||
- name: Brakeman
|
||||
uses: devmasx/brakeman-linter-action@v1.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
REPORT_PATH: tmp/brakeman.json
|
||||
- uses: devmasx/coverage-check-action@coverage-check
|
||||
with:
|
||||
result_path: coverage/.last_run.json
|
||||
token: ${{secrets.GITHUB_TOKEN}}
|
||||
type: simplecov
|
||||
min_coverage: 90
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
21
action.yml
21
action.yml
@ -1,9 +1,24 @@
|
||||
name: "Brakeman linter"
|
||||
description: "A GitHub Action that lints your Ruby code with Brakeman!"
|
||||
author: Miguel Savignano
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "Dockerfile"
|
||||
branding:
|
||||
icon: "check-square"
|
||||
color: "red"
|
||||
inputs:
|
||||
type:
|
||||
description: "simplecov | jest"
|
||||
required: true
|
||||
default: "simplecov"
|
||||
token:
|
||||
description: "Github token for create checks"
|
||||
required: true
|
||||
default: "World"
|
||||
min_coverage:
|
||||
description: "Minimum coverage"
|
||||
default: "80"
|
||||
result_path:
|
||||
description: "Json with coverage result"
|
||||
required: true
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "Dockerfile"
|
||||
|
||||
33
lib/coverage_report.rb
Normal file
33
lib/coverage_report.rb
Normal file
@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CoverageReport
|
||||
def self.generate(type, report_path, data)
|
||||
if type == 'simplecov'
|
||||
simplecov(report_path, data)
|
||||
elsif type == 'jest'
|
||||
jest(report_path, data)
|
||||
else
|
||||
raise 'InvalidCoverageReportType'
|
||||
end
|
||||
end
|
||||
|
||||
def self.simplecov(report_path, data)
|
||||
report = read_json(report_path)
|
||||
minumum_percent = data[:min]
|
||||
covered_percent = report.dig('result', 'covered_percent')
|
||||
{ 'lines' => { 'covered_percent' => covered_percent, 'minumum_percent' => minumum_percent } }
|
||||
end
|
||||
|
||||
def self.jest(report_path, data)
|
||||
report = read_json(report_path)
|
||||
minumum_percent = data[:min]
|
||||
covered_percent = report.dig('result', 'covered_percent')
|
||||
{ 'lines' => { 'covered_percent' => covered_percent, 'minumum_percent' => minumum_percent } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_json(path)
|
||||
JSON.parse(File.read(path))
|
||||
end
|
||||
end
|
||||
@ -1,13 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class GithubCheckRunService
|
||||
CHECK_NAME = 'Brakeman'
|
||||
CHECK_NAME = 'Coverage'
|
||||
|
||||
def initialize(report, github_data, report_adapter)
|
||||
@report = report
|
||||
@github_data = github_data
|
||||
@report_adapter = report_adapter
|
||||
@client = GithubClient.new(@github_data[:token], user_agent: 'brakeman-action')
|
||||
@client = GithubClient.new(@github_data[:token], user_agent: 'coverage-action')
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
14
lib/index.rb
14
lib/index.rb
@ -6,6 +6,7 @@ require 'time'
|
||||
require_relative './report_adapter'
|
||||
require_relative './github_check_run_service'
|
||||
require_relative './github_client'
|
||||
require_relative './coverage_report'
|
||||
|
||||
def read_json(path)
|
||||
JSON.parse(File.read(path))
|
||||
@ -14,16 +15,15 @@ end
|
||||
@event_json = read_json(ENV['GITHUB_EVENT_PATH']) if ENV['GITHUB_EVENT_PATH']
|
||||
@github_data = {
|
||||
sha: ENV['GITHUB_SHA'],
|
||||
token: ENV['GITHUB_TOKEN'],
|
||||
token: ENV['INPUT_TOKEN'],
|
||||
owner: ENV['GITHUB_REPOSITORY_OWNER'] || @event_json.dig('repository', 'owner', 'login'),
|
||||
repo: ENV['GITHUB_REPOSITORY_NAME'] || @event_json.dig('repository', 'name')
|
||||
}
|
||||
|
||||
@report =
|
||||
if ENV['REPORT_PATH']
|
||||
read_json(ENV['REPORT_PATH'])
|
||||
else
|
||||
Dir.chdir(ENV['GITHUB_WORKSPACE']) { JSON.parse(`brakeman -f json`) }
|
||||
end
|
||||
@coverage_type = ENV['INPUT_TYPE']
|
||||
@report_path = ENV['INPUT_RESULT_PATH']
|
||||
@data = { min: ENV['INPUT_MIN_COVERAGE'] }
|
||||
|
||||
@report = CoverageReport.generate(@coverage_type, @report_path, @data)
|
||||
|
||||
GithubCheckRunService.new(@report, @github_data, ReportAdapter).run
|
||||
|
||||
@ -7,40 +7,29 @@ class ReportAdapter
|
||||
ANNOTATION_LEVEL = { notice: 'notice', warning: 'warning', failure: 'failure' }.freeze
|
||||
|
||||
def conslusion(report)
|
||||
return CONCLUSION_TYPES[:failure] if security_warnings(report).positive?
|
||||
|
||||
CONCLUSION_TYPES[:success]
|
||||
lines_covered_percent(report) >= lines_minimum_percent(report).to_f ? CONCLUSION_TYPES[:success] : CONCLUSION_TYPES[:failure]
|
||||
end
|
||||
|
||||
def summary(report)
|
||||
"**Brakeman Report**:\n#{security_warnings(report)} security warnings\n#{check_table(report)}"
|
||||
"**Coverage**:\n\n#{table_head}\n| Lines | #{lines_covered_percent(report)}% | #{lines_minimum_percent(report)}% |\n"
|
||||
end
|
||||
|
||||
def annotations(report)
|
||||
report['warnings'].map do |error|
|
||||
{
|
||||
'path' => error['file'],
|
||||
'start_line' => error['line'],
|
||||
'end_line' => error['line'],
|
||||
'annotation_level' => ANNOTATION_LEVEL[:warning],
|
||||
'title' => "#{error['confidence']} - #{error['check_name']}",
|
||||
'message' => error['message']
|
||||
}
|
||||
end
|
||||
def annotations(_report)
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_table(report)
|
||||
uniq_checks(report).reduce('') { |memo, check| memo + "- [#{check[:check_name]}](#{check[:link]})\n" }
|
||||
def table_head
|
||||
"| Type | covered | minimum |\n| ----- | ------- | ------- |"
|
||||
end
|
||||
|
||||
def uniq_checks(report)
|
||||
report['warnings'].map { |w| { check_name: w['check_name'], link: w['link'] } }.uniq { |w| w[:check_name] }
|
||||
def lines_covered_percent(report)
|
||||
@lines_covered_percent ||= report.dig('lines', 'covered_percent')
|
||||
end
|
||||
|
||||
def security_warnings(report)
|
||||
report['scan_info']['security_warnings']
|
||||
def lines_minimum_percent(report)
|
||||
@lines_minimum_percent ||= report.dig('lines', 'minumum_percent')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
BIN
screenshots/fail.png
Normal file
BIN
screenshots/fail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB |
BIN
screenshots/success.png
Normal file
BIN
screenshots/success.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
34
spec/fixtures/output/annotations.json
vendored
34
spec/fixtures/output/annotations.json
vendored
@ -1,34 +0,0 @@
|
||||
[
|
||||
{
|
||||
"path": "app/controllers/posts_controller.rb",
|
||||
"start_line": 29,
|
||||
"end_line": 29,
|
||||
"annotation_level": "warning",
|
||||
"title": "High - Evaluation",
|
||||
"message": "User input in eval"
|
||||
},
|
||||
{
|
||||
"path": "app/controllers/posts_controller.rb",
|
||||
"start_line": 18,
|
||||
"end_line": 18,
|
||||
"annotation_level": "warning",
|
||||
"title": "High - MassAssignment",
|
||||
"message": "Parameters should be whitelisted for mass assignment"
|
||||
},
|
||||
{
|
||||
"path": "app/controllers/posts_controller.rb",
|
||||
"start_line": 19,
|
||||
"end_line": 19,
|
||||
"annotation_level": "warning",
|
||||
"title": "High - MassAssignment",
|
||||
"message": "Parameters should be whitelisted for mass assignment"
|
||||
},
|
||||
{
|
||||
"path": "app/controllers/posts_controller.rb",
|
||||
"start_line": 13,
|
||||
"end_line": 13,
|
||||
"annotation_level": "warning",
|
||||
"title": "Medium - SQL",
|
||||
"message": "Possible SQL injection"
|
||||
}
|
||||
]
|
||||
10
spec/fixtures/output/summary.md
vendored
10
spec/fixtures/output/summary.md
vendored
@ -1,5 +1,5 @@
|
||||
**Brakeman Report**:
|
||||
4 security warnings
|
||||
- [Evaluation](https://brakemanscanner.org/docs/warning_types/dangerous_eval/)
|
||||
- [MassAssignment](https://brakemanscanner.org/docs/warning_types/mass_assignment/)
|
||||
- [SQL](https://brakemanscanner.org/docs/warning_types/sql_injection/)
|
||||
**Coverage**:
|
||||
|
||||
| Type | covered | minimum |
|
||||
| ----- | ------- | ------- |
|
||||
| Lines | 80% | 80% |
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
require './spec/spec_helper'
|
||||
|
||||
describe GithubCheckRunService do
|
||||
let(:brakeman_report) { JSON(File.read('./spec/fixtures/report.json')) }
|
||||
let(:report) do
|
||||
{ 'lines' => { 'covered_percent' => 80, 'minumum_percent' => 80 } }
|
||||
end
|
||||
let(:github_data) { { sha: 'sha', token: 'token', owner: 'owner', repo: 'repository_name' } }
|
||||
let(:service) { GithubCheckRunService.new(brakeman_report, github_data, ReportAdapter) }
|
||||
let(:service) { GithubCheckRunService.new(report, github_data, ReportAdapter) }
|
||||
|
||||
it '#run' do
|
||||
stub_request(:any, 'https://api.github.com/repos/owner/repository_name/check-runs/id')
|
||||
|
||||
@ -3,12 +3,8 @@
|
||||
require './spec/spec_helper'
|
||||
|
||||
describe ReportAdapter do
|
||||
let(:brakeman_report) do
|
||||
JSON(File.read('./spec/fixtures/report.json'))
|
||||
end
|
||||
|
||||
let(:spec_annotations) do
|
||||
JSON(File.read('./spec/fixtures/output/annotations.json'))
|
||||
let(:report) do
|
||||
{ 'lines' => { 'covered_percent' => 80, 'minumum_percent' => 80 } }
|
||||
end
|
||||
|
||||
let(:spec_summary) do
|
||||
@ -18,17 +14,17 @@ describe ReportAdapter do
|
||||
let(:adapter) { ReportAdapter }
|
||||
|
||||
it '.conslusion' do
|
||||
result = adapter.conslusion(brakeman_report)
|
||||
expect(result).to eq('failure')
|
||||
result = adapter.conslusion(report)
|
||||
expect(result).to eq('success')
|
||||
end
|
||||
|
||||
it '.summary' do
|
||||
result = adapter.summary(brakeman_report)
|
||||
result = adapter.summary(report)
|
||||
expect(result).to eq(spec_summary)
|
||||
end
|
||||
|
||||
it '.annotations' do
|
||||
result = adapter.annotations(brakeman_report)
|
||||
expect(result).to eq(spec_annotations)
|
||||
result = adapter.annotations(report)
|
||||
expect(result).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user