danger-warnings 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ module Warnings
2
+ # Defines severity levels and provides helper methods.
3
+ class Severity
4
+ SEVERITIES = %i(low medium high).freeze
5
+
6
+ def self.valid?(value)
7
+ key = value
8
+ key = value.to_sym if value.method_exists?(:to_sym)
9
+ SEVERITIES.include?(key)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ sonar.projectKey=Kyaak_danger-warnings
2
+ sonar.sources=lib
3
+ sonar.tests=spec
4
+
5
+ sonar.ruby.file.suffixes=rb,ruby
6
+ sonar.ruby.coverage.reportPath=coverage/.resultset.json
7
+ sonar.ruby.coverage.framework=RSpec
8
+ sonar.ruby.rubocopConfig=.rubocop.yml
9
+ sonar.ruby.rubocop.reportPath=rubocop-result.json
@@ -0,0 +1,8 @@
1
+ module Warnings
2
+ module Assets
3
+ ASSETS_DIR = Pathname.new(File.expand_path('.', __dir__))
4
+ BANDIT_JSON = "#{ASSETS_DIR}/bandit.json".freeze
5
+ BANDIT_EMPTY = "#{ASSETS_DIR}/bandit_empty.json".freeze
6
+ BANDIT_MISSING_RESULTS = "#{ASSETS_DIR}/bandit_missing_results.json".freeze
7
+ end
8
+ end
@@ -0,0 +1,74 @@
1
+ {
2
+ "errors": [],
3
+ "generated_at": "2019-01-08T22:29:03Z",
4
+ "metrics": {
5
+ "_totals": {
6
+ "CONFIDENCE.HIGH": 46.0,
7
+ "CONFIDENCE.LOW": 0.0,
8
+ "CONFIDENCE.MEDIUM": 0.0,
9
+ "CONFIDENCE.UNDEFINED": 0.0,
10
+ "SEVERITY.HIGH": 0.0,
11
+ "SEVERITY.LOW": 34.0,
12
+ "SEVERITY.MEDIUM": 12.0,
13
+ "SEVERITY.UNDEFINED": 0.0,
14
+ "loc": 14685,
15
+ "nosec": 0
16
+ },
17
+ "example/CppHeaderParser.py": {
18
+ "CONFIDENCE.HIGH": 29.0,
19
+ "CONFIDENCE.LOW": 0.0,
20
+ "CONFIDENCE.MEDIUM": 0.0,
21
+ "CONFIDENCE.UNDEFINED": 0.0,
22
+ "SEVERITY.HIGH": 0.0,
23
+ "SEVERITY.LOW": 29.0,
24
+ "SEVERITY.MEDIUM": 0.0,
25
+ "SEVERITY.UNDEFINED": 0.0,
26
+ "loc": 2282,
27
+ "nosec": 0
28
+ }
29
+ },
30
+ "results": [
31
+ {
32
+ "code": "2852 except ImportError:\n2853 import pickle\n2854 with open(filename, 'wb') as outf:\n",
33
+ "filename": "example/ply/yacc_1.py",
34
+ "issue_confidence": "HIGH",
35
+ "issue_severity": "LOW",
36
+ "issue_text": "Consider possible security implications associated with pickle module.",
37
+ "line_number": 2853,
38
+ "line_range": [
39
+ 2853
40
+ ],
41
+ "more_info": "https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b403-import-pickle",
42
+ "test_id": "B403",
43
+ "test_name": "blacklist"
44
+ },
45
+ {
46
+ "code": "3254 pkgname = '.'.join(parts[:-1])\n3255 exec('import %s' % pkgname)\n3256 srcfile = getattr(sys.modules[pkgname], '__file__', '')\n",
47
+ "filename": "example/ply/yacc_2.py",
48
+ "issue_confidence": "HIGH",
49
+ "issue_severity": "MEDIUM",
50
+ "issue_text": "Use of exec detected.",
51
+ "line_number": 3255,
52
+ "line_range": [
53
+ 3255
54
+ ],
55
+ "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b102_exec_used.html",
56
+ "test_id": "B102",
57
+ "test_name": "exec_used"
58
+ },
59
+ {
60
+ "code": "3254 pkgname = '.'.join(parts[:-1])\n3255 exec('import %s' % pkgname)\n3256 srcfile = getattr(sys.modules[pkgname], '__file__', '')\n",
61
+ "filename": "example/ply/yacc_3.py",
62
+ "issue_confidence": "HIGH",
63
+ "issue_severity": "HIGH",
64
+ "issue_text": "Use of exec detected.",
65
+ "line_number": 3255,
66
+ "line_range": [
67
+ 3255
68
+ ],
69
+ "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b102_exec_used.html",
70
+ "test_id": "B102",
71
+ "test_name": "exec_used"
72
+ }
73
+ ]
74
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "errors": [],
3
+ "generated_at": "2019-01-08T22:29:03Z",
4
+ "metrics": {
5
+ "_totals": {
6
+ "CONFIDENCE.HIGH": 0.0,
7
+ "CONFIDENCE.LOW": 0.0,
8
+ "CONFIDENCE.MEDIUM": 0.0,
9
+ "CONFIDENCE.UNDEFINED": 0.0,
10
+ "SEVERITY.HIGH": 0.0,
11
+ "SEVERITY.LOW": 0.0,
12
+ "SEVERITY.MEDIUM": 12.0,
13
+ "SEVERITY.UNDEFINED": 0.0,
14
+ "loc": 1000,
15
+ "nosec": 0
16
+ }
17
+ },
18
+ "results": [
19
+ ]
20
+ }
@@ -0,0 +1,2 @@
1
+ {
2
+ }
@@ -0,0 +1,65 @@
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/warnings/markdown_util'
3
+
4
+ module Warnings
5
+ describe Warnings::MarkdownUtil do
6
+ MARKDOWN_TEST_REPORT_NAME = 'My Report Name'.freeze
7
+
8
+ context '#generate' do
9
+ context 'header' do
10
+ it 'adds header name at first line' do
11
+ result = MarkdownUtil.generate(MARKDOWN_TEST_REPORT_NAME, [])
12
+ header_name = result.split(MarkdownUtil::LINE_SEPARATOR).first
13
+ expect(header_name).to eq("# #{MARKDOWN_TEST_REPORT_NAME}")
14
+ end
15
+
16
+ it 'adds table header at second line' do
17
+ result = MarkdownUtil.generate(MARKDOWN_TEST_REPORT_NAME, [])
18
+ table_header = result.split(MarkdownUtil::LINE_SEPARATOR)[1]
19
+ expect(table_header).to eq(MarkdownUtil::TABLE_HEADER)
20
+ end
21
+
22
+ it 'adds table separator at third line' do
23
+ result = MarkdownUtil.generate(MARKDOWN_TEST_REPORT_NAME, [])
24
+ table_header = result.split(MarkdownUtil::LINE_SEPARATOR)[2]
25
+ expect(table_header).to eq(MarkdownUtil::TABLE_SEPARATOR)
26
+ end
27
+ end
28
+
29
+ context 'with issues' do
30
+ before do
31
+ @issue = Issue.new
32
+ @issue.severity = :low
33
+ @issue.name = 'blacklist'
34
+ @issue.file_name = 'hello/test.py'
35
+ @issue.message = 'Consider possible security implications associated with pickle module.'
36
+ @issue.line = 1234
37
+ @issue.id = 'B403'
38
+
39
+ result = MarkdownUtil.generate(MARKDOWN_TEST_REPORT_NAME, [@issue])
40
+ @issue_line = result.split(MarkdownUtil::LINE_SEPARATOR)[3]
41
+ @issue_columns = @issue_line.split(MarkdownUtil::COLUMN_SEPARATOR)
42
+ end
43
+
44
+ it 'first column contains severity upcase' do
45
+ text = @issue_columns[0]
46
+ expect(text).not_to be_nil
47
+ expect(text).to eq(@issue.severity.to_s.capitalize)
48
+ end
49
+
50
+ it 'second column contains filename:line' do
51
+ text = @issue_columns[1]
52
+ expect(text).not_to be_nil
53
+ expect(text).to eq("#{@issue.file_name}:#{@issue.line}")
54
+ end
55
+
56
+ it 'third column contains [id-name]' do
57
+ text = @issue_columns[2]
58
+ expect(text).not_to be_nil
59
+ match = text.match(/^\[#{@issue.id}-#{@issue.name}\]/)
60
+ expect(match).not_to be_nil
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,102 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/warnings/parser/bandit_parser'
3
+
4
+ module Warnings
5
+ describe BanditParser do
6
+ FIRST_ISSUE = {
7
+ code: "2852 except ImportError:\n2853 import pickle\n2854 with open(filename, 'wb') as outf:\n",
8
+ filename: 'example/ply/yacc_1.py',
9
+ issue_confidence: 'HIGH',
10
+ issue_severity: :low,
11
+ issue_text: 'Consider possible security implications associated with pickle module.',
12
+ line_number: 2853,
13
+ line_range: [
14
+ 2853
15
+ ],
16
+ more_info: 'https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b403-import-pickle',
17
+ test_id: 'B403',
18
+ test_name: 'blacklist'
19
+ }.freeze
20
+
21
+ before do
22
+ @parser = BanditParser.new
23
+ end
24
+
25
+ context '#file_types' do
26
+ it 'include json' do
27
+ expect(@parser.file_types).to include(:json)
28
+ end
29
+ end
30
+
31
+ context '#parse' do
32
+ describe 'json' do
33
+ context 'filled results' do
34
+ before do
35
+ @parser.parse(Assets::BANDIT_JSON)
36
+ @issue = @parser.issues[0]
37
+ expect(@issue).not_to be_nil
38
+ end
39
+
40
+ it 'parses issues' do
41
+ expect(@parser.issues).not_to be_empty
42
+ expect(@parser.issues.count).to eq(3)
43
+ end
44
+
45
+ it 'maps name' do
46
+ expect(@issue.file_name).to eq(FIRST_ISSUE[:filename])
47
+ end
48
+
49
+ it 'maps id' do
50
+ expect(@issue.id).to eq(FIRST_ISSUE[:test_id])
51
+ end
52
+
53
+ it 'maps line' do
54
+ expect(@issue.line).to eq(FIRST_ISSUE[:line_number])
55
+ end
56
+
57
+ it 'maps severity' do
58
+ expect(@issue.severity).to eq(FIRST_ISSUE[:issue_severity])
59
+ end
60
+
61
+ it 'maps message' do
62
+ expect(@issue.message).to eq(FIRST_ISSUE[:issue_text])
63
+ end
64
+
65
+ it 'maps name' do
66
+ expect(@issue.name).to eq(FIRST_ISSUE[:test_name])
67
+ end
68
+ end
69
+
70
+ context 'empty results' do
71
+ it 'has no issues' do
72
+ @parser.parse(Assets::BANDIT_EMPTY)
73
+ expect(@parser.issues).to be_empty
74
+ end
75
+ end
76
+
77
+ context 'missing results' do
78
+ it 'raises error' do
79
+ expect { @parser.parse(Assets::BANDIT_MISSING_RESULTS) }.to raise_error(BanditParser::ERROR_MISSING_KEY)
80
+ end
81
+ end
82
+
83
+ context 'missing file' do
84
+ it 'raises error' do
85
+ file_name = 'invalid.json'
86
+ expect { @parser.parse(file_name) }.to raise_error(format(Parser::ERROR_FILE_NOT_EXIST, file_name))
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'unsupported type' do
92
+ it 'raises error' do
93
+ file_name = 'hello.txt'
94
+ ext = File.extname(file_name).delete('.')
95
+ expect { @parser.parse(file_name) }.to raise_error(format(Parser::ERROR_EXT_NOT_SUPPORTED,
96
+ ext,
97
+ @parser.class.name))
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/warnings/parser/parser_factory'
3
+
4
+ module Warnings
5
+ describe ParserFactory do
6
+ context '#get' do
7
+ it 'unknown symbol' do
8
+ expect { ParserFactory.create(:unknown) }.to raise_error('Parser \'unknown\' not supported.')
9
+ end
10
+
11
+ it 'unknown int' do
12
+ expect { ParserFactory.create(123) }.to raise_error('Parser \'123\' not supported.')
13
+ end
14
+
15
+ it 'unknown string' do
16
+ expect { ParserFactory.create('unknown') }.to raise_error('Parser \'unknown\' not supported.')
17
+ end
18
+
19
+ context 'bandit' do
20
+ it 'symbol' do
21
+ result = ParserFactory.create(:bandit)
22
+ expect(result).not_to be_nil
23
+ expect(result).to be_a(BanditParser)
24
+ end
25
+
26
+ it 'string' do
27
+ result = ParserFactory.create('bandit')
28
+ expect(result).not_to be_nil
29
+ expect(result).to be_a(BanditParser)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,255 @@
1
+ require_relative 'spec_helper'
2
+ require_relative '../lib/warnings/reporter'
3
+ require 'danger'
4
+
5
+ module Warnings
6
+ describe Reporter do
7
+ BANDIT_FILE_1 = 'example/ply/yacc_1.py'.freeze
8
+
9
+ before do
10
+ @dangerfile = testing_dangerfile
11
+ @reporter = Reporter.new(@dangerfile)
12
+
13
+ @dangerfile.git.stubs(:modified_files).returns(%w())
14
+ @dangerfile.git.stubs(:added_files).returns(%w())
15
+ end
16
+
17
+ context '#name' do
18
+ it 'returns default' do
19
+ expect(@reporter.name).to eq(Reporter::DEFAULT_NAME)
20
+ end
21
+
22
+ context 'set' do
23
+ REPORTER_TEST_NAME = 'My Test Name Report'.freeze
24
+
25
+ before do
26
+ @reporter.name = REPORTER_TEST_NAME
27
+ end
28
+
29
+ it 'without parser' do
30
+ expect(@reporter.parser).to be_nil
31
+ expect(@reporter.name).to eq(REPORTER_TEST_NAME)
32
+ end
33
+
34
+ it 'with parser' do
35
+ @reporter.parser = :bandit
36
+ expect(@reporter.parser).not_to be_nil
37
+ expect(@reporter.parser_impl).not_to be_nil
38
+ expect(@reporter.name).to eq(REPORTER_TEST_NAME)
39
+ end
40
+ end
41
+
42
+ context 'not set' do
43
+ it 'without parser' do
44
+ expect(@reporter.parser).to be_nil
45
+ expect(@reporter.parser_impl).to be_nil
46
+ expect(@reporter.name).to eq(Reporter::DEFAULT_NAME)
47
+ end
48
+
49
+ it 'with parser' do
50
+ @reporter.parser = :bandit
51
+ expect(@reporter.parser).not_to be_nil
52
+ expect(@reporter.parser_impl).not_to be_nil
53
+ expect(@reporter.name).to eq("#{BanditParser::NAME} #{Reporter::DEFAULT_NAME}")
54
+ end
55
+ end
56
+ end
57
+
58
+ context '#parser' do
59
+ context 'valid name' do
60
+ it 'sets #parser and #parser_impl' do
61
+ @reporter.parser = :bandit
62
+ expect(@reporter.parser).to eq(:bandit)
63
+ expect(@reporter.parser_impl).not_to be_nil
64
+ expect(@reporter.parser_impl).to be_a(BanditParser)
65
+ end
66
+ end
67
+
68
+ context 'invalid name' do
69
+ it 'setter raises error' do
70
+ expect { @reporter.parser = :unknown }.to raise_error(format(ParserFactory::ERROR_NOT_SUPPORTED,
71
+ 'unknown'))
72
+ end
73
+ end
74
+ end
75
+
76
+ context '#report' do
77
+ it 'raises if no parser' do
78
+ expect(@reporter.parser).to be_nil
79
+ expect { @reporter.report }.to raise_error(Reporter::ERROR_PARSER_NOT_SET)
80
+ end
81
+
82
+ it 'raises if no file' do
83
+ @reporter.parser = :bandit
84
+ expect(@reporter.file).to be_nil
85
+ expect { @reporter.report }.to raise_error(Reporter::ERROR_FILE_NOT_SET)
86
+ end
87
+
88
+ it 'does not report markdown if no issues' do
89
+ @reporter.parser = :bandit
90
+ @reporter.file = Assets::BANDIT_EMPTY
91
+ @reporter.report
92
+ @reporter.inline = false
93
+ expect(@dangerfile.status_report[:markdowns]).to be_empty
94
+ expect(@dangerfile.status_report[:warnings]).to be_empty
95
+ expect(@dangerfile.status_report[:messages]).to be_empty
96
+ expect(@dangerfile.status_report[:errors]).to be_empty
97
+ end
98
+
99
+ it 'does not report inline if no issues' do
100
+ @reporter.parser = :bandit
101
+ @reporter.file = Assets::BANDIT_EMPTY
102
+ @reporter.inline = true
103
+ @reporter.report
104
+ expect(@dangerfile.status_report[:markdowns]).to be_empty
105
+ expect(@dangerfile.status_report[:warnings]).to be_empty
106
+ expect(@dangerfile.status_report[:messages]).to be_empty
107
+ expect(@dangerfile.status_report[:errors]).to be_empty
108
+ end
109
+
110
+ context 'inline' do
111
+ before do
112
+ @reporter.parser = :bandit
113
+ @reporter.file = Assets::BANDIT_JSON
114
+ @reporter.filter = false
115
+ end
116
+
117
+ it 'defaults inline false' do
118
+ expect(@reporter.inline).not_to be_truthy
119
+ end
120
+
121
+ it 'inline false generates markdown' do
122
+ @reporter.inline = false
123
+
124
+ @reporter.report
125
+ expect(@dangerfile.status_report[:markdowns]).not_to be_empty
126
+ expect(@dangerfile.status_report[:warnings]).to be_empty
127
+ expect(@dangerfile.status_report[:messages]).to be_empty
128
+ expect(@dangerfile.status_report[:errors]).to be_empty
129
+ end
130
+
131
+ it 'inline true generates warnings for files' do
132
+ @reporter.inline = true
133
+
134
+ @reporter.report
135
+ expect(@dangerfile.status_report[:markdowns]).to be_empty
136
+ expect(@dangerfile.status_report[:warnings]).not_to be_empty
137
+ expect(@dangerfile.status_report[:messages]).to be_empty
138
+ expect(@dangerfile.status_report[:errors]).to be_empty
139
+ expect(@dangerfile.violation_report[:warnings].first.file).not_to be_empty
140
+ expect(@dangerfile.violation_report[:warnings].first.line).not_to eq(0)
141
+ end
142
+ end
143
+
144
+ context 'fail_error' do
145
+ before do
146
+ @reporter.parser = :bandit
147
+ @reporter.file = Assets::BANDIT_JSON
148
+ end
149
+
150
+ it 'default fail_error false' do
151
+ expect(@reporter.fail_error).not_to be_truthy
152
+ end
153
+
154
+ context 'markdown' do
155
+ before do
156
+ @reporter.inline = false
157
+ @reporter.filter = false
158
+ end
159
+
160
+ it 'fail_error false generates no error' do
161
+ @reporter.fail_error = false
162
+
163
+ @reporter.report
164
+ expect(@dangerfile.status_report[:markdowns]).not_to be_empty
165
+ expect(@dangerfile.status_report[:warnings]).to be_empty
166
+ expect(@dangerfile.status_report[:messages]).to be_empty
167
+ expect(@dangerfile.status_report[:errors]).to be_empty
168
+ end
169
+
170
+ it 'fail_error false generates error message' do
171
+ @reporter.fail_error = true
172
+
173
+ @reporter.report
174
+ expect(@dangerfile.status_report[:markdowns]).not_to be_empty
175
+ expect(@dangerfile.status_report[:warnings]).to be_empty
176
+ expect(@dangerfile.status_report[:messages]).to be_empty
177
+ expect(@dangerfile.status_report[:errors]).not_to be_empty
178
+ error = @dangerfile.status_report[:errors].first
179
+ expect(error).not_to eq(Reporter::ERROR_HIGH_SEVERITY)
180
+ end
181
+ end
182
+
183
+ context 'inline' do
184
+ before do
185
+ @reporter.inline = true
186
+ @reporter.filter = false
187
+ end
188
+
189
+ it 'fail_error false generates no error' do
190
+ @reporter.fail_error = false
191
+
192
+ @reporter.report
193
+ expect(@dangerfile.status_report[:markdowns]).to be_empty
194
+ expect(@dangerfile.status_report[:warnings]).not_to be_empty
195
+ expect(@dangerfile.status_report[:messages]).to be_empty
196
+ expect(@dangerfile.status_report[:errors]).to be_empty
197
+ end
198
+
199
+ it 'fail_error false generates error message' do
200
+ @reporter.fail_error = true
201
+
202
+ @reporter.report
203
+ expect(@dangerfile.status_report[:markdowns]).to be_empty
204
+ expect(@dangerfile.status_report[:warnings]).not_to be_empty
205
+ expect(@dangerfile.status_report[:messages]).to be_empty
206
+ expect(@dangerfile.status_report[:errors]).not_to be_empty
207
+ error = @dangerfile.status_report[:errors].first
208
+ expect(error).to include('High')
209
+ end
210
+ end
211
+ end
212
+
213
+ context 'filter' do
214
+ before do
215
+ @reporter.parser = :bandit
216
+ @reporter.file = Assets::BANDIT_JSON
217
+ @dangerfile.git.stubs(:modified_files).returns(%W(#{BANDIT_FILE_1}))
218
+ end
219
+
220
+ it 'defaults filter true' do
221
+ expect(@reporter.filter).to be_truthy
222
+ end
223
+
224
+ it 'filter false takes all issues' do
225
+ @reporter.inline = true
226
+ @reporter.filter = false
227
+
228
+ @reporter.report
229
+ expect(@dangerfile.violation_report[:warnings].size).to eq(3)
230
+ end
231
+
232
+ it 'filter true rejects unmodified files' do
233
+ @reporter.inline = true
234
+ @reporter.filter = true
235
+
236
+ @reporter.report
237
+ expect(@dangerfile.violation_report[:warnings].size).to eq(1)
238
+ expect(@dangerfile.violation_report[:warnings].first.file).to eq(BANDIT_FILE_1)
239
+ end
240
+
241
+ it 'uses baseline if set' do
242
+ directory = 'pre/dir'
243
+ @reporter.inline = true
244
+ @reporter.filter = true
245
+ @reporter.baseline = directory
246
+ @dangerfile.git.stubs(:modified_files).returns(%W(#{directory}/#{BANDIT_FILE_1}))
247
+
248
+ @reporter.report
249
+ expect(@dangerfile.violation_report[:warnings].size).to eq(1)
250
+ expect(@dangerfile.violation_report[:warnings].first.file).to eq(BANDIT_FILE_1)
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end