rubopolis 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7a7ed5bfea758072102a9bbea085269d3acb334d7adcb5dc981fdabdb52c4085
4
+ data.tar.gz: 5ba582879db9e949f97491045bf986a9536c74618d844cf47b7045b20d75c7c7
5
+ SHA512:
6
+ metadata.gz: 0ff04065bc7620d1983b3acbf99c1e9b8bb8168d5f604d030ea23e3b5f7e26f748af07bc29a4ae560d9ac0e40c7baf189b2d8b498ad6dcd0a91c03e2c54fa729
7
+ data.tar.gz: 7c6cce7704b0336b6a4f13c2669f51a63c250b1c3052207d0c95cb4d7cc8215d0b52a01f5527e7ffa4ceac2f9f1f80054e46bf63e36a2708a82425171fae3e32
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # rubopolis
2
+ ## About
3
+ This repo is a collection of useful rubocops for Rails project.
4
+
5
+ These are the list of custom cops in this project right now:
6
+ 1. Migration filename convention
7
+ 2. Time usage
8
+ 3. Query injection prevention
9
+
10
+
11
+ ## Installation
12
+ Add rubopolis gem to you application:
13
+ ```
14
+ TODO
15
+ ```
16
+
17
+ ## Contribute
18
+ Contributions are welcome!
19
+
20
+ Fork the repository
21
+ Write code and tests
22
+ Submit a PR
@@ -0,0 +1,12 @@
1
+ Cop/MigrationFilename:
2
+ Description: 'Enforce the database migration filename is not a future date'
3
+ Enabled: true
4
+ VersionAdded: '0.0.1'
5
+ Cop/QueryInject:
6
+ Description: 'Ensure code does not have an query inject-able code'
7
+ Enabled: true
8
+ VersionAdded: '0.0.1'
9
+ Cop/TimeUsage:
10
+ Description: 'Use `Time.current` or `Date.current` instead for standardisation.'
11
+ Enabled: true
12
+ VersionAdded: '0.0.1'
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ module Rubopolis
6
+ module Cop
7
+ # This cop is used to enforce proper migration filename in codebase
8
+
9
+ # @example
10
+ # # bad
11
+ # Manipulating the filename timestamp to be the future
12
+ #
13
+ # # good
14
+ # System generated filename
15
+ class MigrationFilename < RuboCop::Cop::Base
16
+ include RuboCop::Cop::RangeHelp
17
+
18
+ MSG_FUTURE_TIMESTAMP = 'The name of this file (`%<basename>s`) should not use future timestamp.'
19
+
20
+ def on_new_investigation
21
+ file_path = processed_source.file_path
22
+
23
+ return unless file_path.include?('/db/')
24
+
25
+ for_bad_filename(file_path) { |range, msg| add_offense(range, message: msg) }
26
+ end
27
+
28
+ def for_bad_filename(file_path)
29
+ basename = File.basename(file_path)
30
+
31
+ filename_datetime = basename.split('_', 2).first
32
+ current_timestamp = Time.now.strftime('%Y%m%d%H%M%S')
33
+
34
+ msg = format(MSG_FUTURE_TIMESTAMP, basename: basename) if filename_datetime > current_timestamp
35
+
36
+ yield source_range(processed_source.buffer, 1, 0), msg if msg
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop/cop/mixin/active_record_helper'
4
+
5
+ module Rubopolis
6
+ module Cop
7
+ # This cop is used to identify usages of `find_by` or `where` with string or params inputs.
8
+ # Use either hash or array input to avoid potential SQL injection.
9
+ # Feel free to disable the rule manually if code is assessed to be safe from injection.
10
+
11
+ # @example
12
+ # # bad
13
+ # User.find_by("name = 'Bruce'")
14
+ # User.find_by(params[:name_query])
15
+ #
16
+ # # good
17
+ # User.find_by('name = ?', 'Bruce')
18
+ # User.find_by(['name = ?', 'Bruce'])
19
+ # User.find_by(name: 'Bruce')
20
+ class QueryInjection < RuboCop::Cop::Base
21
+ include RuboCop::Cop::RangeHelp
22
+ include RuboCop::Cop::ActiveRecordHelper
23
+
24
+ MSG = '`%s` should be called with hash or array arguments only: see lib/custom_cops/query_injection'
25
+
26
+ def on_send(node)
27
+ return if node.receiver.nil? && !inherit_active_record_base?(node)
28
+ return unless method?(node)
29
+ return unless where_or_find_by?(node)
30
+ return if acceptable_arg?(node.arguments[0])
31
+
32
+ # when arguments are > 1 strings, it should be templated and are most likely safe.
33
+ return if node.arguments.length > 1
34
+
35
+ range = offense_range(node)
36
+ add_offense(range, message: format(MSG, @method))
37
+ end
38
+
39
+ private
40
+
41
+ def offense_range(node)
42
+ range_between(node.loc.selector.begin_pos, node.arguments[0].loc.expression.end_pos)
43
+ end
44
+
45
+ def method?(node)
46
+ node.arguments.any? && node.respond_to?(:method?)
47
+ end
48
+
49
+ def where_or_find_by?(node)
50
+ if node.method?(:where)
51
+ @method = 'where'
52
+ return true
53
+ elsif node.method?(:find_by)
54
+ @method = 'find_by'
55
+ return true
56
+ end
57
+
58
+ false
59
+ end
60
+
61
+ def array?(node)
62
+ node.is_a?(RuboCop::AST::ArrayNode)
63
+ end
64
+
65
+ def kwarg?(node)
66
+ node.is_a?(RuboCop::AST::HashNode)
67
+ end
68
+
69
+ def acceptable_arg?(node)
70
+ # TODO: current implementation does not allow generated arguments
71
+ # i.e. find_by(function_that_returns_hash)
72
+ #
73
+ # if this is desired:
74
+ # - raise a chore ticket to update this cop
75
+ # - update rubocop_todo.yml to ignore the file in question OR
76
+ # - temporarily disable the rule in the file with an inline comment
77
+
78
+ array?(node) || kwarg?(node)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubopolis
4
+ module Cop
5
+ # This cop is used to identify usages of `Time.zone.now` or `Time.zone.today` and any Time class related arithmetic.
6
+ # Use `Time.current` or `Date.current` instead for standardisation.
7
+ # Feel free to disable the rule manually if necessary.
8
+
9
+ # @example
10
+ # # bad
11
+ # Time.zone.today
12
+ # Time.zone.now
13
+ # Time.current + 2.weeks
14
+ # Time.current - 2.weeks
15
+ #
16
+ # # good
17
+ # Date.current
18
+ # Time.current
19
+ # 2.weeks.ago
20
+ # 2.weeks.since or 2.weeks.from_now
21
+ class TimeUsage < RuboCop::Cop::Base
22
+ USAGE_MSG = '`Time.zone.now` or `Time.zone.today` should not be used, to consider Time.current or ' \
23
+ 'Date.current instead: see lib/custom_cops/time_usage'
24
+
25
+ ARITHMETIC_MSG = 'Avoid subtracting or adding for Time, to use methods like `ago` and `since/from_now` e.g. ' \
26
+ '`2.weeks.ago`, `5.minutes.since`. Refer to https://api.rubyonrails.org/classes/Time.html ' \
27
+ 'for more info'
28
+
29
+ def_node_matcher :time_now?, <<~PATTERN
30
+ (send (send (const nil? :Time) :zone) :now)
31
+ PATTERN
32
+
33
+ def_node_matcher :time_today?, <<~PATTERN
34
+ (send (send (const nil? :Time) :zone) :today)
35
+ PATTERN
36
+
37
+ def_node_matcher :time_addition?, <<~PATTERN
38
+ (send (send (const nil? :Time) :current) :+ $(...))
39
+ PATTERN
40
+
41
+ def_node_matcher :time_subtract?, <<~PATTERN
42
+ (send (send (const nil? :Time) :current) :- $(...))
43
+ PATTERN
44
+
45
+ def on_send(node)
46
+ if time_now?(node) || time_today?(node)
47
+ message = USAGE_MSG
48
+ elsif time_addition?(node) || time_subtract?(node)
49
+ message = ARITHMETIC_MSG
50
+ else
51
+ return
52
+ end
53
+
54
+ add_offense(node, message: message)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ # Original from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
6
+ module Rubopolis
7
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
8
+ # bit of our configuration.
9
+ module Inject
10
+ def self.defaults!
11
+ path = CONFIG_DEFAULT.to_s
12
+ hash = ::RuboCop::ConfigLoader.send(:load_yaml_configuration, path)
13
+ config = ::RuboCop::Config.new(hash, path).tap(&:make_excludes_absolute)
14
+ puts "configuration from #{path}" if ::RuboCop::ConfigLoader.debug?
15
+ config = ::RuboCop::ConfigLoader.merge_with_default(config, path)
16
+ ::RuboCop::ConfigLoader.instance_variable_set(:@default_configuration, config)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubopolis
4
+ module Version
5
+ WRAPPER_VERSION = '0.0.6'
6
+ end
7
+ end
data/lib/rubopolis.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'yaml'
5
+
6
+ require_relative 'rubopolis/inject'
7
+ require_relative 'rubopolis/version'
8
+ require_relative 'rubopolis/cop/migration_filename'
9
+ require_relative 'rubopolis/cop/query_injection'
10
+ require_relative 'rubopolis/cop/time_usage'
11
+
12
+ ##
13
+ # Base Module
14
+ module Rubopolis
15
+ PROJECT_ROOT = ::Pathname.new(__dir__).parent.expand_path.freeze
16
+ CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
17
+ CONFIG = ::YAML.safe_load(CONFIG_DEFAULT.read).freeze
18
+
19
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
20
+ end
21
+
22
+ Rubopolis::Inject.defaults!
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubopolis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Eileen Kang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.33'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.33'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.22'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.22'
69
+ - !ruby/object:Gem::Dependency
70
+ name: timecop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.6
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2'
111
+ description: All custom rubocop for Rails project
112
+ email: eileen_kang@tech.gov.sg
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - README.md
118
+ - config/default.yml
119
+ - lib/rubopolis.rb
120
+ - lib/rubopolis/cop/migration_filename.rb
121
+ - lib/rubopolis/cop/query_injection.rb
122
+ - lib/rubopolis/cop/time_usage.rb
123
+ - lib/rubopolis/inject.rb
124
+ - lib/rubopolis/version.rb
125
+ homepage: https://github.com/GovTechSG/rubopolis
126
+ licenses:
127
+ - MIT
128
+ metadata:
129
+ rubygems_mfa_required: 'true'
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ requirements: []
145
+ rubygems_version: 3.2.33
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Custom rubocop for Rails project
149
+ test_files: []