spektr 0.4.1 → 0.5.0

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +2 -2
  3. data/CHANGELOG.md +4 -0
  4. data/CODE_OF_CONDUCT.md +1 -72
  5. data/lib/spektr/app.rb +2 -48
  6. data/lib/spektr/checks/base.rb +85 -60
  7. data/lib/spektr/checks/basic_auth.rb +2 -1
  8. data/lib/spektr/checks/command_injection.rb +15 -10
  9. data/lib/spektr/checks/content_tag_xss.rb +17 -9
  10. data/lib/spektr/checks/cookie_serialization.rb +1 -1
  11. data/lib/spektr/checks/create_with.rb +3 -3
  12. data/lib/spektr/checks/csrf_setting.rb +4 -18
  13. data/lib/spektr/checks/default_routes.rb +3 -2
  14. data/lib/spektr/checks/deserialize.rb +9 -13
  15. data/lib/spektr/checks/detailed_exceptions.rb +3 -3
  16. data/lib/spektr/checks/dynamic_finders.rb +2 -2
  17. data/lib/spektr/checks/evaluation.rb +2 -2
  18. data/lib/spektr/checks/file_access.rb +3 -3
  19. data/lib/spektr/checks/file_disclosure.rb +1 -1
  20. data/lib/spektr/checks/filter_skipping.rb +3 -1
  21. data/lib/spektr/checks/json_encoding.rb +1 -0
  22. data/lib/spektr/checks/json_entity_escape.rb +8 -5
  23. data/lib/spektr/checks/json_parsing.rb +1 -1
  24. data/lib/spektr/checks/link_to_href.rb +8 -6
  25. data/lib/spektr/checks/mass_assignment.rb +7 -7
  26. data/lib/spektr/checks/send.rb +2 -2
  27. data/lib/spektr/checks/sqli.rb +6 -10
  28. data/lib/spektr/checks/xss.rb +10 -7
  29. data/lib/spektr/extractors/calls.rb +22 -0
  30. data/lib/spektr/extractors/methods.rb +28 -0
  31. data/lib/spektr/targets/base.rb +80 -81
  32. data/lib/spektr/targets/controller.rb +35 -24
  33. data/lib/spektr/targets/routes.rb +1 -1
  34. data/lib/spektr/targets/view.rb +8 -9
  35. data/lib/spektr/version.rb +1 -1
  36. data/lib/spektr/warning.rb +2 -2
  37. data/lib/spektr.rb +8 -8
  38. data/spektr.gemspec +3 -2
  39. metadata +23 -20
  40. data/lib/spektr/exp/assignment.rb +0 -20
  41. data/lib/spektr/exp/base.rb +0 -32
  42. data/lib/spektr/exp/const.rb +0 -7
  43. data/lib/spektr/exp/definition.rb +0 -32
  44. data/lib/spektr/exp/ivasign.rb +0 -7
  45. data/lib/spektr/exp/lvasign.rb +0 -7
  46. data/lib/spektr/exp/send.rb +0 -135
  47. data/lib/spektr/exp/xstr.rb +0 -12
  48. data/lib/spektr/processors/base.rb +0 -87
  49. data/lib/spektr/processors/class_processor.rb +0 -24
@@ -10,12 +10,12 @@ module Spektr
10
10
  @location = location
11
11
  @message = message
12
12
  @confidence = confidence
13
- @line = IO.readlines(full_path)[@location.line - 1].strip if full_path && @location && File.exist?(full_path)
13
+ @line = IO.readlines(full_path)[@location.start_line - 1].strip if full_path && @location && File.exist?(full_path)
14
14
  end
15
15
 
16
16
  def full_message
17
17
  if @location
18
- "#{message} at line #{@location.line} of #{@path}"
18
+ "#{message} at line #{@location.start_line} of #{@path}"
19
19
  else
20
20
  "#{message}"
21
21
  end
data/lib/spektr.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler'
4
- require 'parser'
5
- require 'parser/current'
4
+ require 'prism'
6
5
  require 'erb'
6
+ require 'herb'
7
7
  require 'haml'
8
8
  require 'logger'
9
9
  require 'tty/spinner'
@@ -23,12 +23,12 @@ module Spektr
23
23
  @output_format = output_format
24
24
  start_spinner('Initializing')
25
25
  @log_level = if debug
26
- Logger::DEBUG
27
- elsif terminal?
28
- Logger::ERROR
29
- else
30
- Logger::WARN
31
- end
26
+ Logger::DEBUG
27
+ elsif terminal?
28
+ Logger::ERROR
29
+ else
30
+ Logger::WARN
31
+ end
32
32
  checks = Checks.load(checks)
33
33
  root = './' if root.nil?
34
34
  @app = App.new(checks: checks, root: root, ignore: ignore)
data/spektr.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
18
- spec.metadata["source_code_uri"] = "https://github.com/gregmolnar/spektr"
18
+ spec.metadata['source_code_uri'] = 'https://github.com/gregmolnar/spektr'
19
19
  # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
@@ -28,9 +28,10 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ['lib']
29
29
 
30
30
  spec.add_dependency 'erubi'
31
+ spec.add_dependency 'herb'
31
32
  spec.add_dependency 'haml'
32
- spec.add_dependency 'parser', '>= 2.6.0'
33
33
  spec.add_dependency 'pastel'
34
+ spec.add_dependency 'prism'
34
35
  spec.add_dependency 'ruby_parser', '>= 3.0'
35
36
  spec.add_dependency 'slim'
36
37
  spec.add_dependency 'tty-color'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spektr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Molnar
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-03-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: erubi
@@ -25,7 +24,7 @@ dependencies:
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: haml
27
+ name: herb
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
@@ -39,19 +38,19 @@ dependencies:
39
38
  - !ruby/object:Gem::Version
40
39
  version: '0'
41
40
  - !ruby/object:Gem::Dependency
42
- name: parser
41
+ name: haml
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: 2.6.0
46
+ version: '0'
48
47
  type: :runtime
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: 2.6.0
53
+ version: '0'
55
54
  - !ruby/object:Gem::Dependency
56
55
  name: pastel
57
56
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +65,20 @@ dependencies:
66
65
  - - ">="
67
66
  - !ruby/object:Gem::Version
68
67
  version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: prism
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
69
82
  - !ruby/object:Gem::Dependency
70
83
  name: ruby_parser
71
84
  requirement: !ruby/object:Gem::Requirement
@@ -303,16 +316,8 @@ files:
303
316
  - lib/spektr/cli.rb
304
317
  - lib/spektr/core_ext/string.rb
305
318
  - lib/spektr/erubi.rb
306
- - lib/spektr/exp/assignment.rb
307
- - lib/spektr/exp/base.rb
308
- - lib/spektr/exp/const.rb
309
- - lib/spektr/exp/definition.rb
310
- - lib/spektr/exp/ivasign.rb
311
- - lib/spektr/exp/lvasign.rb
312
- - lib/spektr/exp/send.rb
313
- - lib/spektr/exp/xstr.rb
314
- - lib/spektr/processors/base.rb
315
- - lib/spektr/processors/class_processor.rb
319
+ - lib/spektr/extractors/calls.rb
320
+ - lib/spektr/extractors/methods.rb
316
321
  - lib/spektr/targets/base.rb
317
322
  - lib/spektr/targets/config.rb
318
323
  - lib/spektr/targets/controller.rb
@@ -329,7 +334,6 @@ licenses:
329
334
  metadata:
330
335
  homepage_uri: https://spektrhq.com
331
336
  source_code_uri: https://github.com/gregmolnar/spektr
332
- post_install_message:
333
337
  rdoc_options: []
334
338
  require_paths:
335
339
  - lib
@@ -344,8 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
348
  - !ruby/object:Gem::Version
345
349
  version: '0'
346
350
  requirements: []
347
- rubygems_version: 3.4.6
348
- signing_key:
351
+ rubygems_version: 3.6.9
349
352
  specification_version: 4
350
353
  summary: Rails static code analyzer for security issues
351
354
  test_files: []
@@ -1,20 +0,0 @@
1
- module Spektr
2
- module Exp
3
- module Assignment
4
- def user_input?
5
- if ast.children[1].type == :send
6
- _send = Send.new(ast.children[1])
7
- name = if _send.receiver && _send.receiver.type == :send
8
- _send.receiver.name
9
- else
10
- nil
11
- end
12
- if [:params, :cookies, :request].include? name
13
- return true
14
- end
15
- end
16
- false
17
- end
18
- end
19
- end
20
- end
@@ -1,32 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Base
4
- attr_accessor :ast, :name, :type, :options, :arguments, :body, :location
5
-
6
- def initialize(ast)
7
- @ast = ast
8
- @type = ast.type
9
- @location = ast.location
10
- @name = ast.children.first
11
- @options = {}
12
- @arguments = []
13
- @body = []
14
- end
15
-
16
- def send?
17
- is_a? Send
18
- end
19
-
20
- include AST::Processor::Mixin
21
-
22
- def process(ast)
23
- return unless ast.respond_to?(:to_ast)
24
- super
25
- end
26
-
27
- def handler_missing(node)
28
- # puts "handler missing for #{node.type}"
29
- end
30
- end
31
- end
32
- end
@@ -1,7 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Const < Base
4
- include Spektr::Exp::Assignment
5
- end
6
- end
7
- end
@@ -1,32 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Definition < Base
4
- attr_accessor :private, :protected
5
-
6
- def initialize(ast)
7
- super
8
- process @ast.children
9
- end
10
-
11
- def process(ast)
12
- ast&.each do |node|
13
- next unless Parser::AST::Node === node
14
- case node.type
15
- when :args
16
- node.children.each do |argument|
17
- @arguments << argument.children.first
18
- end
19
- when :begin
20
- process(node.children)
21
- when :lvasgn
22
- @body << Lvasign.new(node)
23
- when :ivasgn
24
- @body << Ivasign.new(node)
25
- when :send
26
- @body << Send.new(node)
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,7 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Ivasign < Base
4
- include Spektr::Exp::Assignment
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Lvasign < Base
4
- include Spektr::Exp::Assignment
5
- end
6
- end
7
- end
@@ -1,135 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Send < Base
4
- attr_accessor :receiver
5
-
6
- def initialize(ast)
7
- super
8
- @receiver = Receiver.new(ast.children[0]) if ast.children[0]
9
- @name = if ast.children.first.is_a?(Parser::AST::Node)
10
- ast.children.first.children.last
11
- else
12
- ast.children[1]
13
- end
14
- children = ast.children[2..]
15
- children.each do |child|
16
- next unless child.is_a?(Parser::AST::Node)
17
-
18
- case child.type
19
- when :hash
20
- if children.size == 1 || children.last == child
21
- child.children.each do |pair|
22
- @options[pair.children[0].children[0]] = Option.new(pair)
23
- end
24
- else
25
- @arguments << Argument.new(child)
26
- end
27
- else
28
- @arguments << Argument.new(child)
29
- end
30
- end
31
- end
32
- end
33
-
34
- class Argument < Base
35
- attr_accessor :name, :type, :ast, :children
36
-
37
- def initialize(ast)
38
- @name = nil
39
- process_ast(ast)
40
- ast = ast.children.first if ast.type == :begin
41
- @ast = ast
42
- argument = if %i[xstr hash].include? ast.type
43
- ast
44
- elsif ast.type != :dstr && ast.children.first.is_a?(Parser::AST::Node) && ast.children.first.children.first.is_a?(Parser::AST::Node)
45
- ast.children.first.children.first
46
- elsif ast.type != :dstr && ast.children.first.is_a?(Parser::AST::Node)
47
- ast.children.first
48
- else
49
- ast
50
- end
51
- @type = argument.type
52
- @children = argument.children
53
- end
54
-
55
- def process_ast(ast)
56
- process(ast)
57
- end
58
-
59
- def on_begin(node)
60
- process_all(node)
61
- end
62
-
63
- def on_send(node)
64
- if node.children.first.nil?
65
- @name ||= node.children[1]
66
- elsif node.is_a?(Parser::AST::Node)
67
- process_all(node)
68
- end
69
- end
70
-
71
- def on_const(node)
72
- @name ||= node.children[1] if node.children.first.nil?
73
- end
74
-
75
- def on_str(node)
76
- @name ||= node.children.first
77
- end
78
-
79
- alias on_sym on_str
80
- alias on_ivar on_str
81
- end
82
-
83
- class Option
84
- attr_accessor :name, :key, :value, :type, :value_name, :value_type
85
-
86
- def initialize(ast)
87
- @key = ast.children.first
88
- @name = ast.children.first.children.last
89
- @type = ast.type
90
-
91
- @value = ast.children.last
92
- @value_name = ast.children.last.children.last
93
- @value_type = ast.children.last.type
94
- end
95
- end
96
-
97
- class Receiver
98
- attr_accessor :name, :type, :ast, :expanded, :children
99
-
100
- def initialize(ast)
101
- @children = []
102
- set_attributes(ast)
103
- end
104
-
105
- def set_attributes(ast)
106
- if [:begin].include?(ast.type) && ast.children[0].is_a?(Parser::AST::Node)
107
- return set_attributes(ast.children[0])
108
- end
109
-
110
- @ast = ast
111
- if ast.type == :dstr
112
- @type = :dstr
113
- @name = ast.children[0].children.first
114
- ast.children[1..].each do |ch|
115
- @children << Receiver.new(ch)
116
- end
117
- else
118
- @expanded = expand!(ast)
119
- @ast = ast.type == :send && ast.children[0].is_a?(Parser::AST::Node) ? ast.children[0] : ast
120
- @type = @ast.type
121
- @name = @ast.children.last
122
- end
123
- end
124
-
125
- def expand!(ast, tree = [])
126
- if ast.is_a?(Parser::AST::Node) && ast.children.any?
127
- tree << ast.children.last
128
- expand!(ast.children.first, tree)
129
- else
130
- tree.reverse.join('.')
131
- end
132
- end
133
- end
134
- end
135
- end
@@ -1,12 +0,0 @@
1
- module Spektr
2
- module Exp
3
- class Xstr < Base
4
- def initialize(ast)
5
- super
6
- ast.children[1..].each do |child|
7
- @arguments << Argument.new(child)
8
- end
9
- end
10
- end
11
- end
12
- end
@@ -1,87 +0,0 @@
1
- require 'ast'
2
- module Spektr::Processors
3
- class Base
4
- include AST::Processor::Mixin
5
- attr_accessor :name, :name_parts, :parent_parts, :parent_modules, :parent_name, :parent_name_with_modules
6
-
7
- def initialize
8
- @modules = []
9
- @name_parts = []
10
- @parent_parts = []
11
- @parent_modules = []
12
- end
13
-
14
- def name
15
- @name_parts.join('::')
16
- end
17
-
18
- def parent_name
19
- parent_parts.join('::')
20
- end
21
-
22
- def parent_parts
23
- result = @parent_parts.dup
24
- result.pop if part_matches_self?(result.last.to_s)
25
- result
26
- end
27
-
28
- def part_matches_self?(part)
29
- (part == name || part_with_module(part) == name)
30
- end
31
-
32
- def part_with_module(part)
33
- (@parent_modules | [part]).join('::')
34
- end
35
-
36
- def parent_name_with_modules
37
- parts = @parent_modules | parent_parts
38
- parts.join('::')
39
- end
40
-
41
- def on_begin(node)
42
- process_all(node)
43
- end
44
-
45
- def on_module(node)
46
- parts = extract_name_part(node)
47
- @modules.concat(parts)
48
- @name_parts.concat(parts)
49
- @parent_modules << node.children.first.children.last
50
- process_all(node)
51
- end
52
-
53
- def extract_parent_parts(node)
54
- return unless node.is_a?(Parser::AST::Node) && %i[ module class const send].include?(node.type)
55
- @parent_parts.prepend(node.children.last) if node.type == :const
56
- if node.children.any?
57
- node.children.each do |child|
58
- extract_parent_parts(child)
59
- end
60
- end
61
- end
62
-
63
- def on_class(node)
64
- extract_parent_parts(node)
65
- @name_parts.concat(extract_name_part(node))
66
- process_all(node)
67
- end
68
-
69
- def extract_name_part(node)
70
- parts = []
71
- node.children.first.children.each do |child|
72
- if child.is_a?(Parser::AST::Node)
73
- parts << child.children.last
74
- elsif child.is_a? Symbol
75
- parts << child.to_s
76
- end
77
- end
78
- parts
79
- end
80
-
81
- def on_const(node); end
82
-
83
- def handler_missing(node)
84
- # puts "handler missing for #{node.type}"
85
- end
86
- end
87
- end
@@ -1,24 +0,0 @@
1
- module Spektr
2
- module Processors
3
- class ClassProcessor < Base
4
- include AST::Processor::Mixin
5
-
6
- attr_accessor :data
7
-
8
- def on_begin(node)
9
- end
10
-
11
- def on_def(node)
12
- puts "on def: #{node.inspect}"
13
- end
14
-
15
- def on_require(node)
16
- puts "on require: #{node.inspect}"
17
- end
18
-
19
- def on_class(node)
20
- puts "on class2: #{node.inspect}"
21
- end
22
- end
23
- end
24
- end