cfn-nag 0.8.0 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cfn_nag_rules +1 -1
- data/lib/cfn-nag/base_rule.rb +7 -1
- data/lib/cfn-nag/cfn_nag.rb +10 -13
- data/lib/cfn-nag/cfn_nag_executor.rb +2 -2
- data/lib/cfn-nag/cli_options.rb +4 -3
- data/lib/cfn-nag/custom_rules/base.rb +8 -1
- data/lib/cfn-nag/result_view/colored_stdout_results.rb +11 -2
- data/lib/cfn-nag/result_view/json_results.rb +1 -1
- data/lib/cfn-nag/result_view/sarif_results.rb +103 -0
- data/lib/cfn-nag/result_view/simple_stdout_results.rb +11 -2
- data/lib/cfn-nag/result_view/stdout_results.rb +3 -2
- data/lib/cfn-nag/rule_definition.rb +6 -3
- data/lib/cfn-nag/rule_registry.rb +1 -0
- data/lib/cfn-nag/version.rb +6 -0
- data/lib/cfn-nag/violation.rb +17 -3
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bcd7a2b51e44dcad5d3d5f0514cd1f670913c0ce8074f6af6dfe9b9c5c28c4e
|
4
|
+
data.tar.gz: 5bc5eba04f176db1d734aa7d32806f53d9f55402e128b56c5a50e6367974ef4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '028736d712ade96898ea014e859955bd94d68012e2cdca469b5dbf1b38cd86b42ec316710f06353e5ff5d82895a75df330830a149a3e9ecfc750a4f2bf108c88'
|
7
|
+
data.tar.gz: f02924cf126fc681df943225f2d2231628d6309feed5dbf7db67aab0ae53ef466d6e4e6236a820bfe677946b75cc47b9e1f087c8ad3dda2fb97bcc4c6cab60ce
|
data/bin/cfn_nag_rules
CHANGED
data/lib/cfn-nag/base_rule.rb
CHANGED
@@ -20,10 +20,16 @@ class CfnNag
|
|
20
20
|
logical_resource_ids = audit_impl(cfn_model)
|
21
21
|
return if logical_resource_ids.empty?
|
22
22
|
|
23
|
+
violation(logical_resource_ids)
|
24
|
+
end
|
25
|
+
|
26
|
+
def violation(logical_resource_ids, line_numbers = nil)
|
23
27
|
Violation.new(id: rule_id,
|
28
|
+
name: self.class.name,
|
24
29
|
type: rule_type,
|
25
30
|
message: rule_text,
|
26
|
-
logical_resource_ids: logical_resource_ids
|
31
|
+
logical_resource_ids: logical_resource_ids,
|
32
|
+
line_numbers: line_numbers)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
data/lib/cfn-nag/cfn_nag.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative 'result_view/stdout_results'
|
|
8
8
|
require_relative 'result_view/simple_stdout_results'
|
9
9
|
require_relative 'result_view/colored_stdout_results'
|
10
10
|
require_relative 'result_view/json_results'
|
11
|
+
require_relative 'result_view/sarif_results'
|
11
12
|
require 'cfn-model'
|
12
13
|
|
13
14
|
# Top-level CfnNag class for running profiles
|
@@ -94,12 +95,12 @@ class CfnNag
|
|
94
95
|
)
|
95
96
|
|
96
97
|
violations = filter_violations_by_deny_list_and_profile(violations)
|
97
|
-
violations =
|
98
|
+
violations = mark_line_numbers_and_element_types(violations, cfn_model)
|
98
99
|
rescue RuleRepoException, Psych::SyntaxError, ParserError => fatal_error
|
99
|
-
violations << fatal_violation(fatal_error.to_s)
|
100
|
+
violations << Violation.fatal_violation(fatal_error.to_s)
|
100
101
|
rescue JSON::ParserError => json_parameters_error
|
101
102
|
error = "JSON Parameter values parse error: #{json_parameters_error}"
|
102
|
-
violations << fatal_violation(error)
|
103
|
+
violations << Violation.fatal_violation(error)
|
103
104
|
end
|
104
105
|
|
105
106
|
violations = prune_fatal_violations(violations) if @config.ignore_fatal
|
@@ -112,15 +113,16 @@ class CfnNag
|
|
112
113
|
|
113
114
|
def render_results(aggregate_results:,
|
114
115
|
output_format:)
|
115
|
-
results_renderer(output_format).new.render(aggregate_results)
|
116
|
+
results_renderer(output_format).new.render(aggregate_results, @config.custom_rule_loader.rule_definitions)
|
116
117
|
end
|
117
118
|
|
118
119
|
private
|
119
120
|
|
120
|
-
def
|
121
|
+
def mark_line_numbers_and_element_types(violations, cfn_model)
|
121
122
|
violations.each do |violation|
|
122
123
|
violation.logical_resource_ids.each do |logical_resource_id|
|
123
124
|
violation.line_numbers << cfn_model.line_numbers[logical_resource_id]
|
125
|
+
violation.element_types << cfn_model.element_types[logical_resource_id]
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
@@ -141,7 +143,7 @@ class CfnNag
|
|
141
143
|
violations: violations
|
142
144
|
)
|
143
145
|
rescue StandardError => deny_list_or_profile_parse_error
|
144
|
-
violations << fatal_violation(deny_list_or_profile_parse_error.to_s)
|
146
|
+
violations << Violation.fatal_violation(deny_list_or_profile_parse_error.to_s)
|
145
147
|
violations
|
146
148
|
end
|
147
149
|
|
@@ -152,17 +154,12 @@ class CfnNag
|
|
152
154
|
}
|
153
155
|
end
|
154
156
|
|
155
|
-
def fatal_violation(message)
|
156
|
-
Violation.new(id: 'FATAL',
|
157
|
-
type: Violation::FAILING_VIOLATION,
|
158
|
-
message: message)
|
159
|
-
end
|
160
|
-
|
161
157
|
def results_renderer(output_format)
|
162
158
|
registry = {
|
163
159
|
'colortxt' => ColoredStdoutResults,
|
164
160
|
'txt' => SimpleStdoutResults,
|
165
|
-
'json' => JsonResults
|
161
|
+
'json' => JsonResults,
|
162
|
+
'sarif' => SarifResults
|
166
163
|
}
|
167
164
|
registry[output_format]
|
168
165
|
end
|
@@ -74,9 +74,9 @@ class CfnNagExecutor
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def validate_options(opts)
|
77
|
-
unless opts[:output_format].nil? || %w[colortxt txt json].include?(opts[:output_format])
|
77
|
+
unless opts[:output_format].nil? || %w[colortxt txt json sarif].include?(opts[:output_format])
|
78
78
|
Optimist.die(:output_format,
|
79
|
-
'Must be colortxt, txt, or
|
79
|
+
'Must be colortxt, txt, json or sarif')
|
80
80
|
end
|
81
81
|
|
82
82
|
opts[:rule_arguments]&.each do |rule_argument|
|
data/lib/cfn-nag/cli_options.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'optimist'
|
4
|
+
require_relative 'version'
|
4
5
|
|
5
6
|
# rubocop:disable Metrics/ClassLength
|
6
7
|
class Options
|
@@ -8,7 +9,7 @@ class Options
|
|
8
9
|
'emit the exception without stack trace ' \
|
9
10
|
'and keep chugging'
|
10
11
|
|
11
|
-
@version =
|
12
|
+
@version = CfnNagVersion::VERSION
|
12
13
|
|
13
14
|
def self.for(type)
|
14
15
|
case type
|
@@ -89,7 +90,7 @@ class Options
|
|
89
90
|
required: false,
|
90
91
|
default: false
|
91
92
|
opt :output_format,
|
92
|
-
'Format of results: [txt, json, colortxt]',
|
93
|
+
'Format of results: [txt, json, colortxt, sarif]',
|
93
94
|
type: :string,
|
94
95
|
default: 'colortxt'
|
95
96
|
opt :rule_repository,
|
@@ -132,7 +133,7 @@ class Options
|
|
132
133
|
type: :string,
|
133
134
|
required: true
|
134
135
|
opt :output_format,
|
135
|
-
'Format of results: [txt, json, colortxt]',
|
136
|
+
'Format of results: [txt, json, colortxt, sarif]',
|
136
137
|
type: :string,
|
137
138
|
default: 'colortxt'
|
138
139
|
opt :debug,
|
@@ -19,9 +19,16 @@ class BaseRule
|
|
19
19
|
logical_resource_ids = audit_impl(cfn_model)
|
20
20
|
return if logical_resource_ids.empty?
|
21
21
|
|
22
|
+
violation(logical_resource_ids)
|
23
|
+
end
|
24
|
+
|
25
|
+
def violation(logical_resource_ids, line_numbers = [], element_types = [])
|
22
26
|
Violation.new(id: rule_id,
|
27
|
+
name: self.class.name,
|
23
28
|
type: rule_type,
|
24
29
|
message: rule_text,
|
25
|
-
logical_resource_ids: logical_resource_ids
|
30
|
+
logical_resource_ids: logical_resource_ids,
|
31
|
+
line_numbers: line_numbers,
|
32
|
+
element_types: element_types)
|
26
33
|
end
|
27
34
|
end
|
@@ -10,7 +10,8 @@ class ColoredStdoutResults < StdoutResults
|
|
10
10
|
color:,
|
11
11
|
message:,
|
12
12
|
logical_resource_ids: nil,
|
13
|
-
line_numbers: []
|
13
|
+
line_numbers: [],
|
14
|
+
element_types: [])
|
14
15
|
|
15
16
|
logical_resource_ids = nil if logical_resource_ids == []
|
16
17
|
|
@@ -18,7 +19,7 @@ class ColoredStdoutResults < StdoutResults
|
|
18
19
|
puts
|
19
20
|
puts colorize(color, "| #{message_type.upcase}")
|
20
21
|
puts colorize(color, '|')
|
21
|
-
puts colorize(color, "|
|
22
|
+
puts colorize(color, "| #{element_type(element_types)}: #{logical_resource_ids}") unless logical_resource_ids.nil?
|
22
23
|
puts colorize(color, "| Line Numbers: #{line_numbers}") unless line_numbers.empty?
|
23
24
|
puts colorize(color, '|') unless line_numbers.empty? && logical_resource_ids.nil?
|
24
25
|
puts colorize(color, "| #{message}")
|
@@ -38,4 +39,12 @@ class ColoredStdoutResults < StdoutResults
|
|
38
39
|
def colorize(color_symbol, str)
|
39
40
|
"\e[#{color_code(color_symbol)}m#{str}\e[0m"
|
40
41
|
end
|
42
|
+
|
43
|
+
def element_type(element_types)
|
44
|
+
if element_types == [] || element_types.first.nil?
|
45
|
+
'Element'
|
46
|
+
elsif !element_types.first.nil?
|
47
|
+
element_types.first.capitalize
|
48
|
+
end
|
49
|
+
end
|
41
50
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
class SarifResults
|
7
|
+
def render(results, rule_registry)
|
8
|
+
sarif_results = []
|
9
|
+
results.each do |file|
|
10
|
+
# For each file in the results, review the violations
|
11
|
+
file[:file_results][:violations].each do |violation|
|
12
|
+
# For each violation, generate a sarif result for each logical resource id in the violation
|
13
|
+
violation.logical_resource_ids.each_with_index do |_logical_resource_id, index|
|
14
|
+
sarif_results << sarif_result(file_name: file[:filename], violation: violation, index: index)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
sarif_report = {
|
20
|
+
version: '2.1.0',
|
21
|
+
'$schema': 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
22
|
+
runs: [
|
23
|
+
tool: {
|
24
|
+
driver: driver(rule_registry.rules)
|
25
|
+
},
|
26
|
+
results: sarif_results
|
27
|
+
]
|
28
|
+
}
|
29
|
+
|
30
|
+
puts JSON.pretty_generate(sarif_report)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generates a SARIF driver object, which describes the tool and the rules used
|
34
|
+
def driver(rules)
|
35
|
+
{
|
36
|
+
name: 'cfn_nag',
|
37
|
+
informationUri: 'https://github.com/stelligent/cfn_nag',
|
38
|
+
semanticVersion: CfnNagVersion::VERSION,
|
39
|
+
rules: rules.map do |rule_definition|
|
40
|
+
{
|
41
|
+
id: "CFN_NAG_#{rule_definition.id}",
|
42
|
+
name: rule_definition.name,
|
43
|
+
fullDescription: {
|
44
|
+
text: rule_definition.message
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Given a cfn_nag Violation object, and index, generates a SARIF result object for the finding
|
52
|
+
def sarif_result(file_name:, violation:, index:)
|
53
|
+
{
|
54
|
+
ruleId: "CFN_NAG_#{violation.id}",
|
55
|
+
level: sarif_level(violation.type),
|
56
|
+
message: {
|
57
|
+
text: violation.message
|
58
|
+
},
|
59
|
+
locations: [
|
60
|
+
{
|
61
|
+
physicalLocation: {
|
62
|
+
artifactLocation: {
|
63
|
+
uri: relative_path(file_name),
|
64
|
+
uriBaseId: '%SRCROOT%'
|
65
|
+
},
|
66
|
+
region: {
|
67
|
+
startLine: sarif_line_number(violation.line_numbers[index])
|
68
|
+
}
|
69
|
+
},
|
70
|
+
logicalLocations: [
|
71
|
+
{
|
72
|
+
name: violation.logical_resource_ids[index]
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
# Line number defaults to 1 unless provided with valid number
|
81
|
+
def sarif_line_number(line_number)
|
82
|
+
line_number.nil? || line_number.to_i < 1 ? 1 : line_number.to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def sarif_level(violation_type)
|
86
|
+
case violation_type
|
87
|
+
when RuleDefinition::WARNING
|
88
|
+
'warning'
|
89
|
+
else
|
90
|
+
'error'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def relative_path(file_name)
|
95
|
+
file_pathname = Pathname.new(file_name)
|
96
|
+
|
97
|
+
if file_pathname.relative?
|
98
|
+
file_pathname.to_s
|
99
|
+
else
|
100
|
+
file_pathname.relative_path_from(Pathname.pwd).to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -11,7 +11,8 @@ class SimpleStdoutResults < StdoutResults
|
|
11
11
|
message:,
|
12
12
|
color:,
|
13
13
|
logical_resource_ids: nil,
|
14
|
-
line_numbers: []
|
14
|
+
line_numbers: [],
|
15
|
+
element_types: [])
|
15
16
|
|
16
17
|
logical_resource_ids = nil if logical_resource_ids == []
|
17
18
|
|
@@ -19,10 +20,18 @@ class SimpleStdoutResults < StdoutResults
|
|
19
20
|
puts
|
20
21
|
puts "| #{message_type.upcase}"
|
21
22
|
puts '|'
|
22
|
-
puts "|
|
23
|
+
puts "| #{element_type(element_types)}: #{logical_resource_ids}" unless logical_resource_ids.nil?
|
23
24
|
puts "| Line Numbers: #{line_numbers}" unless line_numbers.empty?
|
24
25
|
puts '|' unless line_numbers.empty? && logical_resource_ids.nil?
|
25
26
|
puts "| #{message}"
|
26
27
|
end
|
27
28
|
# rubocop:enable Lint/UnusedMethodArgument
|
29
|
+
|
30
|
+
def element_type(element_types)
|
31
|
+
if element_types == [] || element_types.first.nil?
|
32
|
+
'Element'
|
33
|
+
elsif !element_types.first.nil?
|
34
|
+
element_types.first.capitalize
|
35
|
+
end
|
36
|
+
end
|
28
37
|
end
|
@@ -12,7 +12,8 @@ class StdoutResults
|
|
12
12
|
color: color,
|
13
13
|
message: violation.message,
|
14
14
|
logical_resource_ids: violation.logical_resource_ids,
|
15
|
-
line_numbers: violation.line_numbers
|
15
|
+
line_numbers: violation.line_numbers,
|
16
|
+
element_types: violation.element_types
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -24,7 +25,7 @@ class StdoutResults
|
|
24
25
|
puts "Warnings count: #{Violation.count_warnings(violations)}"
|
25
26
|
end
|
26
27
|
|
27
|
-
def render(results)
|
28
|
+
def render(results, _rule_definitions)
|
28
29
|
results.each do |result|
|
29
30
|
60.times { print '-' }
|
30
31
|
puts "\n#{result[:filename]}"
|
@@ -4,27 +4,30 @@ class RuleDefinition
|
|
4
4
|
WARNING = 'WARN'
|
5
5
|
FAILING_VIOLATION = 'FAIL'
|
6
6
|
|
7
|
-
attr_reader :id, :type, :message
|
7
|
+
attr_reader :id, :name, :type, :message
|
8
8
|
|
9
9
|
def initialize(id:,
|
10
|
+
name:,
|
10
11
|
type:,
|
11
12
|
message:)
|
12
13
|
@id = id
|
14
|
+
@name = name
|
13
15
|
@type = type
|
14
16
|
@message = message
|
15
17
|
|
16
|
-
[@id, @type, @message].each do |required|
|
18
|
+
[@id, @type, @name, @message].each do |required|
|
17
19
|
raise 'No parameters to Violation constructor can be nil' if required.nil?
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
23
|
def to_s
|
22
|
-
"#{@id} #{@type} #{@message}"
|
24
|
+
"#{@id} #{name} #{@type} #{@message}"
|
23
25
|
end
|
24
26
|
|
25
27
|
def to_h
|
26
28
|
{
|
27
29
|
id: @id,
|
30
|
+
name: @name,
|
28
31
|
type: @type,
|
29
32
|
message: @message
|
30
33
|
}
|
data/lib/cfn-nag/violation.rb
CHANGED
@@ -4,20 +4,26 @@ require_relative 'rule_definition'
|
|
4
4
|
|
5
5
|
# Rule definition for violations
|
6
6
|
class Violation < RuleDefinition
|
7
|
-
attr_reader :logical_resource_ids, :line_numbers
|
7
|
+
attr_reader :logical_resource_ids, :line_numbers, :element_types
|
8
8
|
|
9
|
+
# rubocop:disable Metrics/ParameterLists
|
9
10
|
def initialize(id:,
|
11
|
+
name:,
|
10
12
|
type:,
|
11
13
|
message:,
|
12
14
|
logical_resource_ids: [],
|
13
|
-
line_numbers: []
|
15
|
+
line_numbers: [],
|
16
|
+
element_types: [])
|
14
17
|
super id: id,
|
18
|
+
name: name,
|
15
19
|
type: type,
|
16
20
|
message: message
|
17
21
|
|
18
22
|
@logical_resource_ids = logical_resource_ids
|
19
23
|
@line_numbers = line_numbers
|
24
|
+
@element_types = element_types
|
20
25
|
end
|
26
|
+
# rubocop:enable Metrics/ParameterLists
|
21
27
|
|
22
28
|
def to_s
|
23
29
|
"#{super} #{@logical_resource_ids}"
|
@@ -26,7 +32,8 @@ class Violation < RuleDefinition
|
|
26
32
|
def to_h
|
27
33
|
super.to_h.merge(
|
28
34
|
logical_resource_ids: @logical_resource_ids,
|
29
|
-
line_numbers: @line_numbers
|
35
|
+
line_numbers: @line_numbers,
|
36
|
+
element_types: @element_types
|
30
37
|
)
|
31
38
|
end
|
32
39
|
|
@@ -57,6 +64,13 @@ class Violation < RuleDefinition
|
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
67
|
+
def fatal_violation(message)
|
68
|
+
Violation.new(id: 'FATAL',
|
69
|
+
name: 'system',
|
70
|
+
type: Violation::FAILING_VIOLATION,
|
71
|
+
message: message)
|
72
|
+
end
|
73
|
+
|
60
74
|
private
|
61
75
|
|
62
76
|
def empty?(array)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfn-nag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Kascic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.6.
|
75
|
+
version: 0.6.6
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.6.
|
82
|
+
version: 0.6.6
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: logging
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -371,6 +371,7 @@ files:
|
|
371
371
|
- lib/cfn-nag/result_view/colored_stdout_results.rb
|
372
372
|
- lib/cfn-nag/result_view/json_results.rb
|
373
373
|
- lib/cfn-nag/result_view/rules_view.rb
|
374
|
+
- lib/cfn-nag/result_view/sarif_results.rb
|
374
375
|
- lib/cfn-nag/result_view/simple_stdout_results.rb
|
375
376
|
- lib/cfn-nag/result_view/stdout_results.rb
|
376
377
|
- lib/cfn-nag/rule_definition.rb
|
@@ -388,6 +389,7 @@ files:
|
|
388
389
|
- lib/cfn-nag/util/enforce_string_or_dynamic_reference.rb
|
389
390
|
- lib/cfn-nag/util/truthy.rb
|
390
391
|
- lib/cfn-nag/util/wildcard_patterns.rb
|
392
|
+
- lib/cfn-nag/version.rb
|
391
393
|
- lib/cfn-nag/violation.rb
|
392
394
|
- lib/cfn-nag/violation_filtering.rb
|
393
395
|
homepage: https://github.com/stelligent/cfn_nag
|