sequra-style 1.13.0 → 1.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/default.yml +13 -0
- data/lib/rubocop/cop/sequra/no_sidekiq_perform_stubs.rb +77 -0
- data/lib/sequra/style/version.rb +1 -1
- data/lib/sequra_style.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3d6244fe18ea0a416aa78a3f808972f72e8951a5b1f4b8fe6d8bde547519a062
|
|
4
|
+
data.tar.gz: c6a4e0354de8afabe25ab07ab269109e07b572ef76f746ed8e194b2063e35c08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48b797b66b13747ec048dd26121a5c8ca225e694e13527fdad286e825b42dd29d0177cf9cdf0f4bd7f10b674538b318e95a4538b2122a9dd5d0a3a3196c2b0ea
|
|
7
|
+
data.tar.gz: 80f63fb2c00f3f295fec302fb748aaa30de666ec002aa666a5bd2f10e98197f283f70f0d5400b6212ac4d926b5eac8e04a554f0a012cca0c1c5c047a0a531947
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.14.0](https://github.com/sequra/sequra-style/compare/v1.13.0...v1.14.0) (2026-05-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* [COR-1992] Add Sequra/NoSidekiqPerformStubs cop ([#70](https://github.com/sequra/sequra-style/issues/70)) ([1a89ca4](https://github.com/sequra/sequra-style/commit/1a89ca4a9bcf2f12837f4d54d4aa5f4e7d54c899))
|
|
9
|
+
|
|
3
10
|
## [1.13.0](https://github.com/sequra/sequra-style/compare/v1.12.1...v1.13.0) (2026-04-24)
|
|
4
11
|
|
|
5
12
|
|
data/Gemfile.lock
CHANGED
data/default.yml
CHANGED
|
@@ -832,3 +832,16 @@ Sequra/AsyncJobPattern:
|
|
|
832
832
|
- "packs/*/app/workers/**/*.rb"
|
|
833
833
|
Exclude:
|
|
834
834
|
- "**/application_job.rb"
|
|
835
|
+
|
|
836
|
+
# Forbid stubbing/spying on Sidekiq enqueue methods in specs. Stubs bypass
|
|
837
|
+
# `Sidekiq.strict_args!` validation, which has caused silent production
|
|
838
|
+
# failures (COR-1923). Use `have_enqueued_sidekiq_job` or
|
|
839
|
+
# `change(Job.jobs, :size)` instead. `.and_call_original` is exempt.
|
|
840
|
+
# Ships disabled so consumer repos opt in on their own timeline (paired with
|
|
841
|
+
# `.rubocop_todo.yml` to grandfather existing offenses).
|
|
842
|
+
Sequra/NoSidekiqPerformStubs:
|
|
843
|
+
Enabled: false
|
|
844
|
+
Include:
|
|
845
|
+
- "**/*_spec.rb"
|
|
846
|
+
- "**/spec/support/**/*.rb"
|
|
847
|
+
- "**/spec/shared_examples/**/*.rb"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module RuboCop
|
|
2
|
+
module Cop
|
|
3
|
+
module Sequra
|
|
4
|
+
# Detects RSpec stubs and spy assertions on Sidekiq enqueue methods
|
|
5
|
+
# (`perform_async`, `perform_at`, `perform_in`).
|
|
6
|
+
#
|
|
7
|
+
# Stubbing these methods bypasses `Sidekiq.strict_args!` validation,
|
|
8
|
+
# which lets symbol-keyed hashes (and other non-JSON-native types)
|
|
9
|
+
# slip through unchecked. The same code would raise `ArgumentError`
|
|
10
|
+
# in production at enqueue time, and the bug only surfaces when the
|
|
11
|
+
# job actually runs — by which point retries, timing windows, and
|
|
12
|
+
# rescue-swallowers can mask the failure. This pattern caused a
|
|
13
|
+
# silent push-notification drop in COR-1923.
|
|
14
|
+
#
|
|
15
|
+
# Use `have_enqueued_sidekiq_job` (or `change(SomeJob.jobs, :size)`)
|
|
16
|
+
# instead. Both go through Sidekiq's client middleware chain, so
|
|
17
|
+
# `strict_args!` validates the arguments as it would in production.
|
|
18
|
+
#
|
|
19
|
+
# `.and_call_original` is exempt: the stub spies on the call but
|
|
20
|
+
# then runs the real `perform_*`, which still exercises the
|
|
21
|
+
# serialization contract.
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# # bad - stub bypasses strict_args! validation
|
|
25
|
+
# allow(SomeJob).to receive(:perform_async)
|
|
26
|
+
#
|
|
27
|
+
# # bad - even with arg matchers, the real enqueue is suppressed
|
|
28
|
+
# expect(SomeJob).to receive(:perform_at).with(time, data)
|
|
29
|
+
#
|
|
30
|
+
# # bad - negative assertion still bypasses the contract
|
|
31
|
+
# expect(SomeJob).not_to receive(:perform_async)
|
|
32
|
+
#
|
|
33
|
+
# # bad - spy verification on a stubbed enqueue
|
|
34
|
+
# expect(SomeJob).to have_received(:perform_async).with(id)
|
|
35
|
+
#
|
|
36
|
+
# # good - exercises strict_args! through Sidekiq's middleware
|
|
37
|
+
# expect(SomeJob).to have_enqueued_sidekiq_job(id, name: "value")
|
|
38
|
+
#
|
|
39
|
+
# # good - state-based assertion, also exercises the middleware
|
|
40
|
+
# expect { subject }.to change(SomeJob.jobs, :size).by(1)
|
|
41
|
+
#
|
|
42
|
+
# # good - escape hatch: spy AND run the real method
|
|
43
|
+
# expect(SomeJob).to receive(:perform_async).and_call_original
|
|
44
|
+
#
|
|
45
|
+
class NoSidekiqPerformStubs < Base
|
|
46
|
+
MSG = "Avoid stubbing Sidekiq enqueue methods (`perform_async`/`perform_at`/`perform_in`). " \
|
|
47
|
+
"Stubs bypass `Sidekiq.strict_args!` validation and can hide symbol-keyed hash bugs " \
|
|
48
|
+
"(see COR-1923). Use `have_enqueued_sidekiq_job` or `change(Job.jobs, :size)` instead. " \
|
|
49
|
+
"Use `.and_call_original` only as an escape hatch.".freeze
|
|
50
|
+
|
|
51
|
+
def_node_matcher :perform_method_stub?, <<~PATTERN
|
|
52
|
+
(send nil? {:receive :have_received} (sym {:perform_async :perform_at :perform_in}))
|
|
53
|
+
PATTERN
|
|
54
|
+
|
|
55
|
+
def on_send(node)
|
|
56
|
+
return unless perform_method_stub?(node)
|
|
57
|
+
return if chained_with_call_original?(node)
|
|
58
|
+
|
|
59
|
+
add_offense(node)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def chained_with_call_original?(node)
|
|
65
|
+
current = node
|
|
66
|
+
loop do
|
|
67
|
+
parent = current.parent
|
|
68
|
+
return false unless parent&.send_type? && parent.receiver == current
|
|
69
|
+
return true if parent.method_name == :and_call_original
|
|
70
|
+
|
|
71
|
+
current = parent
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/sequra/style/version.rb
CHANGED
data/lib/sequra_style.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequra-style
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sequra engineering
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|
|
@@ -135,6 +135,7 @@ files:
|
|
|
135
135
|
- docs/decisions/index.md
|
|
136
136
|
- docs/decisions/template.md
|
|
137
137
|
- lib/rubocop/cop/sequra/async_job_pattern.rb
|
|
138
|
+
- lib/rubocop/cop/sequra/no_sidekiq_perform_stubs.rb
|
|
138
139
|
- lib/sequra/style.rb
|
|
139
140
|
- lib/sequra/style/version.rb
|
|
140
141
|
- lib/sequra_style.rb
|