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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4c41df0b3f754ff3eed8026a43244578095abe1ce06be4c89a2457af83c718b
4
- data.tar.gz: 7c648f6838985bc45f6bb2a4f600ea93c05b8f10b1078a0e83b936c7e7449e59
3
+ metadata.gz: 8bcd7a2b51e44dcad5d3d5f0514cd1f670913c0ce8074f6af6dfe9b9c5c28c4e
4
+ data.tar.gz: 5bc5eba04f176db1d734aa7d32806f53d9f55402e128b56c5a50e6367974ef4f
5
5
  SHA512:
6
- metadata.gz: 1e90784cea9ee178aed35ae2f0bc27ecaeb3115307bd21553a45b2504dcd2e02aa561d9dd6150fdc7f2e1ee96632488bc6160422df7c7a1a7a466e1e5ceed585
7
- data.tar.gz: d2937c0bf6c1b4d2326b6ab11ea68149c8635de02722d93ac4d6c5824d8fa1b885ce2691d30901044a3f889019227956faaaec489609178d4e1f63812e036de9
6
+ metadata.gz: '028736d712ade96898ea014e859955bd94d68012e2cdca469b5dbf1b38cd86b42ec316710f06353e5ff5d82895a75df330830a149a3e9ecfc750a4f2bf108c88'
7
+ data.tar.gz: f02924cf126fc681df943225f2d2231628d6309feed5dbf7db67aab0ae53ef466d6e4e6236a820bfe677946b75cc47b9e1f087c8ad3dda2fb97bcc4c6cab60ce
data/bin/cfn_nag_rules CHANGED
@@ -6,7 +6,7 @@ require 'cfn-nag'
6
6
  require 'rubygems/specification'
7
7
 
8
8
  opts = Optimist.options do
9
- version Gem::Specification.find_by_name('cfn-nag').version
9
+ version CfnNagVersion::VERSION
10
10
 
11
11
  opt :rule_directory, 'Extra rule directories', type: :io,
12
12
  required: false,
@@ -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
@@ -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 = mark_line_numbers(violations, cfn_model)
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 mark_line_numbers(violations, cfn_model)
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 json')
79
+ 'Must be colortxt, txt, json or sarif')
80
80
  end
81
81
 
82
82
  opts[:rule_arguments]&.each do |rule_argument|
@@ -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 = Gem::Specification.find_by_name('cfn-nag').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, "| Resources: #{logical_resource_ids}") unless logical_resource_ids.nil?
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
@@ -3,7 +3,7 @@
3
3
  require 'json'
4
4
 
5
5
  class JsonResults
6
- def render(results)
6
+ def render(results, _rule_registry)
7
7
  hashified_results = results.each do |result|
8
8
  result[:file_results][:violations] = result[:file_results][:violations].map(&:to_h)
9
9
  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 "| Resources: #{logical_resource_ids}" unless logical_resource_ids.nil?
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
  }
@@ -43,6 +43,7 @@ class RuleRegistry
43
43
  if existing_def.nil?
44
44
  rule_definition = RuleDefinition.new(
45
45
  id: rule.rule_id,
46
+ name: rule_class.name,
46
47
  type: rule.rule_type,
47
48
  message: rule.rule_text
48
49
  )
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CfnNagVersion
4
+ # This is managed at release time via scripts/publish.sh
5
+ VERSION = '0.8.4'
6
+ end
@@ -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.0
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-07 00:00:00.000000000 Z
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.3
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.3
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