rubocop-gusto 10.6.0 → 10.7.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 +5 -0
- data/config/default.yml +7 -2
- data/lib/rubocop/cop/rack/lowercase_header_keys.rb +70 -50
- data/lib/rubocop/gusto/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a5ad5381e46b28ac74b61ba851caa498b964314ad364503d887bdb44908eace6
|
|
4
|
+
data.tar.gz: 9dc15d64da91e12a36ff62f122ca7bc57a47251f2e6e12dc128fe27d7d4d6419
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecb903c72b7a302c2c9627d4e30d77fcbb5e736d8d82e581cf810e3c64ef3a2ccddd57200e204d22092a194d50773ba4181d0ff7172358ac429ee96fed6f9f2d
|
|
7
|
+
data.tar.gz: ab58cc3ea05f13ba3ad600921ef6cf128e5e848d9a4762aac1112ff4cd493636cf69a13f76c59b8c961f464cd9a9ddad43b358eed6d56791e18230817cb91def
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
## Pending
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## 10.7.0
|
|
5
|
+
|
|
6
|
+
- Improve `Rack/LowercaseHeaderKeys` for Rack 3 migration
|
|
7
|
+
- Change `Style/StringLiterals` `EnforcedStyle` from `always` to `always_true`
|
|
8
|
+
|
|
4
9
|
## 10.6.0
|
|
5
10
|
|
|
6
11
|
- Add `Rack/LowercaseHeaderKeys` cop to detect and autocorrect uppercase HTTP response header keys
|
data/config/default.yml
CHANGED
|
@@ -430,14 +430,19 @@ RSpec/SubjectStub:
|
|
|
430
430
|
Enabled: false
|
|
431
431
|
|
|
432
432
|
Rack/LowercaseHeaderKeys:
|
|
433
|
-
Description: 'HTTP response header keys should be lowercase for
|
|
433
|
+
Description: 'HTTP response header keys should be lowercase for HTTP/2 and modern web standards compatibility.'
|
|
434
434
|
Enabled: true
|
|
435
435
|
Include:
|
|
436
436
|
- 'lib/middleware/**/*.rb'
|
|
437
437
|
- 'app/middleware/**/*.rb'
|
|
438
438
|
- 'app/controllers/**/*.rb'
|
|
439
|
+
- 'app/public/**/*controller*.rb'
|
|
440
|
+
- 'app/helpers/**/*.rb'
|
|
439
441
|
- 'packs/**/middleware/**/*.rb'
|
|
440
442
|
- 'packs/**/controllers/**/*.rb'
|
|
443
|
+
- 'packs/**/app/public/**/*controller*.rb'
|
|
444
|
+
- 'packs/**/app/helpers/**/*.rb'
|
|
445
|
+
- 'packs/**/lib/**/*_app.rb'
|
|
441
446
|
- 'components/**/middleware/**/*.rb'
|
|
442
447
|
- 'components/**/controllers/**/*.rb'
|
|
443
448
|
- 'config/initializers/**/*.rb'
|
|
@@ -565,7 +570,7 @@ Style/FormatStringToken:
|
|
|
565
570
|
EnforcedStyle: template
|
|
566
571
|
|
|
567
572
|
Style/FrozenStringLiteralComment:
|
|
568
|
-
EnforcedStyle:
|
|
573
|
+
EnforcedStyle: always_true
|
|
569
574
|
Enabled: true
|
|
570
575
|
|
|
571
576
|
# This can make lines longer and impair readability
|
|
@@ -4,91 +4,111 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Rack
|
|
6
6
|
# Detects HTTP response headers with uppercase characters.
|
|
7
|
-
# HTTP response header keys should be lowercase for
|
|
8
|
-
#
|
|
7
|
+
# HTTP response header keys should be lowercase for compatibility
|
|
8
|
+
# with HTTP/2 and modern web standards.
|
|
9
|
+
#
|
|
10
|
+
# HTTP/2 (RFC 9113) requires all header field names to be lowercase.
|
|
11
|
+
# Using lowercase keys ensures compatibility across HTTP versions
|
|
12
|
+
# and Rack implementations.
|
|
9
13
|
#
|
|
10
14
|
# @example
|
|
11
15
|
# # bad
|
|
12
16
|
# headers['Content-Type'] = 'application/json'
|
|
13
17
|
# response.headers['Location'] = '/redirect'
|
|
18
|
+
# response.set_header('X-Custom', 'value')
|
|
19
|
+
# response.headers['Content-Security-Policy'] += policy
|
|
14
20
|
#
|
|
15
21
|
# # good
|
|
16
22
|
# headers['content-type'] = 'application/json'
|
|
17
23
|
# response.headers['location'] = '/redirect'
|
|
24
|
+
# response.set_header('x-custom', 'value')
|
|
25
|
+
# response.headers['content-security-policy'] += policy
|
|
18
26
|
#
|
|
19
27
|
class LowercaseHeaderKeys < Base
|
|
20
28
|
extend AutoCorrector
|
|
21
29
|
|
|
22
30
|
MSG = "HTTP response header keys should be lowercase. Use `%{downcased}` instead of `%{original}`."
|
|
23
|
-
RESTRICT_ON_SEND = %i([]=).freeze
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Expires Forwarded From Host If-Match If-Modified-Since
|
|
39
|
-
If-None-Match If-Range If-Unmodified-Since Last-Modified
|
|
40
|
-
Link Location Max-Forwards Origin Pragma Proxy-Authenticate
|
|
41
|
-
Proxy-Authorization Range Referer Referrer-Policy Retry-After
|
|
42
|
-
Server Set-Cookie SOAPAction Strict-Transport-Security TE Trailer
|
|
43
|
-
Transfer-Encoding Upgrade User-Agent Vary Via Warning
|
|
44
|
-
WWW-Authenticate X-Content-Type-Options X-Frame-Options
|
|
45
|
-
X-XSS-Protection X-Forwarded-For X-Forwarded-Host X-Forwarded-Proto
|
|
46
|
-
X-Real-IP X-Request-ID X-Request-Start X-Requested-With
|
|
47
|
-
).map(&:downcase)
|
|
48
|
-
).freeze
|
|
31
|
+
RESTRICT_ON_SEND = %i([]= [] set_header get_header delete_header has_header?).freeze
|
|
32
|
+
|
|
33
|
+
# @!method response_header_method?(node)
|
|
34
|
+
def_node_matcher :response_header_method?, <<~PATTERN
|
|
35
|
+
(send
|
|
36
|
+
{
|
|
37
|
+
(send nil? :response)
|
|
38
|
+
(lvar :response)
|
|
39
|
+
(self)
|
|
40
|
+
}
|
|
41
|
+
{:set_header :get_header :delete_header :has_header?}
|
|
42
|
+
(str _)
|
|
43
|
+
...
|
|
44
|
+
)
|
|
45
|
+
PATTERN
|
|
49
46
|
|
|
50
47
|
def on_send(node)
|
|
51
|
-
|
|
48
|
+
if node.method?(:[]=) || node.method?(:[])
|
|
49
|
+
check_bracket_access(node)
|
|
50
|
+
elsif response_header_method?(node)
|
|
51
|
+
check_header_method(node)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
alias_method :on_csend, :on_send
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
# Handle compound assignment: headers['Key'] += val
|
|
57
|
+
# Ruby parses this as an op_asgn node, not a []= send
|
|
58
|
+
def on_op_asgn(node)
|
|
59
|
+
lhs = node.children[0]
|
|
60
|
+
return unless lhs.send_type? && lhs.method?(:[])
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
return unless
|
|
62
|
+
receiver = lhs.receiver
|
|
63
|
+
return unless receiver.send_type? && response_headers_receiver?(receiver)
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
key_node = lhs.first_argument
|
|
66
|
+
return unless key_node.str_type?
|
|
67
|
+
|
|
68
|
+
check_key(key_node)
|
|
60
69
|
end
|
|
61
|
-
alias_method :on_csend, :on_send
|
|
62
70
|
|
|
63
71
|
private
|
|
64
72
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
return
|
|
73
|
+
def check_bracket_access(node)
|
|
74
|
+
receiver = node.receiver
|
|
75
|
+
return unless receiver.send_type? && response_headers_receiver?(receiver)
|
|
76
|
+
|
|
77
|
+
key_node = node.first_argument
|
|
78
|
+
return unless key_node.str_type?
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
check_key(key_node)
|
|
70
81
|
end
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# conn.headers, request.headers, client.headers, etc.
|
|
77
|
-
def headers_assignment?(node)
|
|
78
|
-
receiver = node.receiver
|
|
79
|
-
# RESTRICT_ON_SEND ensures we only see []=, which always has a receiver
|
|
80
|
-
return false unless receiver.send_type?
|
|
83
|
+
def check_header_method(node)
|
|
84
|
+
key_node = node.first_argument
|
|
85
|
+
check_key(key_node)
|
|
86
|
+
end
|
|
81
87
|
|
|
82
|
-
|
|
88
|
+
def check_key(key_node)
|
|
89
|
+
key_value = key_node.value
|
|
90
|
+
return if key_value.empty?
|
|
91
|
+
return if key_value == key_value.downcase
|
|
92
|
+
|
|
93
|
+
add_offense_for_header(key_node, key_value)
|
|
83
94
|
end
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
# Matches headers receivers that are response-related:
|
|
97
|
+
# headers (bare method call in controller)
|
|
98
|
+
# response.headers
|
|
99
|
+
# self.headers
|
|
100
|
+
# Does NOT match:
|
|
101
|
+
# conn.headers, request.headers, client.headers, etc.
|
|
102
|
+
def response_headers_receiver?(receiver)
|
|
86
103
|
return false unless receiver.method?(:headers)
|
|
87
104
|
|
|
88
105
|
inner = receiver.receiver
|
|
89
106
|
if inner.nil?
|
|
90
107
|
# Bare `headers` method call (Rails controller helper)
|
|
91
108
|
true
|
|
109
|
+
elsif inner.self_type?
|
|
110
|
+
# `self.headers`
|
|
111
|
+
true
|
|
92
112
|
elsif inner.send_type? && inner.receiver.nil? && inner.method?(:response)
|
|
93
113
|
# `response.headers`
|
|
94
114
|
true
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-gusto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 10.
|
|
4
|
+
version: 10.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gusto Engineering
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-03-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: lint_roller
|