rubopolis 0.0.6

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 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: []