dispatch-rails 0.10.1 → 0.10.2
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 +18 -0
- data/app/views/dispatch/_error_tracker.html.erb +8 -8
- data/app/views/dispatch/_widget.html.erb +7 -9
- data/lib/dispatch/rails/event_builder.rb +11 -3
- data/lib/dispatch/rails/reporter.rb +2 -1
- data/lib/dispatch/rails/reporting_endpoint_middleware.rb +7 -1
- data/lib/dispatch/rails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f742aa0ca8db65f9db9c6b5a135ae7d926fdf57dffde8d1d011ae3449298d32
|
|
4
|
+
data.tar.gz: e7b3df1d90a904e8c2383a03d3231c0ff7398b4f269632d697c5d728c42c386a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b9cb75b00f87e29843b6837051100d1cac96448ab6db77a048837303b62cd98d18a1ab7dac1bd1f25a8f527b17cf644f2dab6ef46a4a62cf7edc88f1b02d71fd
|
|
7
|
+
data.tar.gz: f409e09011d0492ccf0ea168ac6f89129ee6fcb83431617a5560ea977c1872a4346586996679538db4b14ea3ae50396d95d47df42f4626f77fff59cd3a00d804
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to `dispatch-rails` are documented here. The format is based
|
|
|
4
4
|
on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.10.2] - 2026-06-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- The browser tracker/widget bootstrapped via an **inline** `<script>` (a nonced
|
|
11
|
+
`javascript_tag` that ran `import("/dispatch/…")`). Inline scripts are fragile
|
|
12
|
+
under Turbo + CSP — the nonce attribute is blanked after parse and must be
|
|
13
|
+
re-applied from the `csp-nonce` meta tag on every navigation — so hosts kept
|
|
14
|
+
seeing `script-src-elem blocked inline`. Both loaders are now plain **external
|
|
15
|
+
same-origin** `<script type="module" src="/dispatch/…">` tags, which
|
|
16
|
+
`script-src 'self'` allows outright — no nonce, no `'unsafe-inline'`, no Turbo
|
|
17
|
+
nonce drift. The nonce is still attached when the host generates one, so strict
|
|
18
|
+
nonce-only / `strict-dynamic` policies keep working. (`type="module"` also lets
|
|
19
|
+
the browser de-dupe the load across Turbo navigations via the module map.)
|
|
20
|
+
- Browser CSP/Reporting-API events were attributed to the `/dispatch/reports`
|
|
21
|
+
endpoint the browser POSTs to, collapsing every violation across the app onto
|
|
22
|
+
one bogus URL. CSP reports now carry the violation's `document-uri` as the event
|
|
23
|
+
URL, so the dashboard groups and links them to the page that actually violated.
|
|
24
|
+
|
|
7
25
|
## [0.10.1] - 2026-06-18
|
|
8
26
|
|
|
9
27
|
### Fixed
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script type="application/json" id="dispatch-error-config"><%= raw error_config_json.gsub("</", "<\\/") %></script>
|
|
2
|
-
<%#
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
<%# The config island above is a non-executable application/json data block, so CSP
|
|
3
|
+
never applies to it. The tracker itself loads as an EXTERNAL same-origin script
|
|
4
|
+
(served by the engine's AssetMiddleware at this exact path), which script-src
|
|
5
|
+
'self' allows on its own — no nonce, no 'unsafe-inline', and none of the inline-
|
|
6
|
+
script-under-Turbo nonce drift that an inline loader is prone to. We still attach
|
|
7
|
+
the nonce when the host generates one, so strict nonce-only / strict-dynamic
|
|
8
|
+
policies (where 'self' alone isn't enough) keep working. %>
|
|
9
|
+
<%= tag.script type: "module", src: "/dispatch/error_tracker.js", nonce: content_security_policy_nonce %>
|
|
@@ -60,12 +60,10 @@
|
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
62
|
|
|
63
|
-
<%#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
<% end %>
|
|
63
|
+
<%# Loaded as an EXTERNAL same-origin module (served by the engine's AssetMiddleware
|
|
64
|
+
at this exact path), so script-src 'self' allows it without a nonce or
|
|
65
|
+
'unsafe-inline' — no inline loader to be blocked under script-src-elem. type=module
|
|
66
|
+
both resolves widget.js's `import "@hotwired/stimulus"` via the host importmap and
|
|
67
|
+
de-dupes the load across Turbo navigations via the module map. The nonce is
|
|
68
|
+
attached when the host generates one, for strict nonce-only / strict-dynamic CSPs. %>
|
|
69
|
+
<%= tag.script type: "module", src: "/dispatch/widget.js", nonce: content_security_policy_nonce %>
|
|
@@ -16,17 +16,23 @@ module Dispatch
|
|
|
16
16
|
MAX_PARAMS_BYTES = 8_000
|
|
17
17
|
SAFE_HEADERS = %w[User-Agent Referer Accept Content-Type Host X-Request-Id].freeze
|
|
18
18
|
|
|
19
|
-
def self.call(exception, handled:, env: nil, user: nil, tags: {}, level: "error")
|
|
20
|
-
new(exception, handled: handled, env: env, user: user, tags: tags, level: level
|
|
19
|
+
def self.call(exception, handled:, env: nil, user: nil, tags: {}, level: "error", request_url: nil)
|
|
20
|
+
new(exception, handled: handled, env: env, user: user, tags: tags, level: level,
|
|
21
|
+
request_url: request_url).call
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def initialize(exception, handled:, env: nil, user: nil, tags: {}, level: "error")
|
|
24
|
+
def initialize(exception, handled:, env: nil, user: nil, tags: {}, level: "error", request_url: nil)
|
|
24
25
|
@exception = exception
|
|
25
26
|
@handled = handled
|
|
26
27
|
@env = env
|
|
27
28
|
@user = user
|
|
28
29
|
@tags = tags || {}
|
|
29
30
|
@level = level
|
|
31
|
+
# Override for the event's request URL. A browser report (CSP, NEL, …) is
|
|
32
|
+
# POSTed to /dispatch/reports, so the env URL is that endpoint, not the page
|
|
33
|
+
# that violated the policy — the caller passes the report's document-uri here
|
|
34
|
+
# so the dashboard attributes (and groups) the event to the real page.
|
|
35
|
+
@request_url_override = request_url
|
|
30
36
|
@source_cache = {}
|
|
31
37
|
@config = Dispatch::Rails.configuration
|
|
32
38
|
end
|
|
@@ -221,6 +227,8 @@ module Dispatch
|
|
|
221
227
|
end
|
|
222
228
|
|
|
223
229
|
def request_url
|
|
230
|
+
return @request_url_override if @request_url_override.present?
|
|
231
|
+
|
|
224
232
|
scheme = @env["rack.url_scheme"] || "http"
|
|
225
233
|
host = @env["HTTP_HOST"] || @env["SERVER_NAME"]
|
|
226
234
|
return nil if host.nil?
|
|
@@ -21,7 +21,8 @@ module Dispatch
|
|
|
21
21
|
user = resolve_user(config, env, context)
|
|
22
22
|
tags = merged_tags(config, env, context)
|
|
23
23
|
event = EventBuilder.call(exception, handled: handled, env: env, user: user,
|
|
24
|
-
tags: tags, level: level
|
|
24
|
+
tags: tags, level: level,
|
|
25
|
+
request_url: context[:request_url])
|
|
25
26
|
event = config.before_send.call(event) if config.before_send.respond_to?(:call)
|
|
26
27
|
return if event.nil?
|
|
27
28
|
|
|
@@ -100,7 +100,13 @@ module Dispatch
|
|
|
100
100
|
handled: true, env: env,
|
|
101
101
|
# report-only disposition is monitoring, not a live block — keep it quieter.
|
|
102
102
|
level: fields[:disposition].to_s == "report" ? "info" : "warning",
|
|
103
|
-
context: {
|
|
103
|
+
context: {
|
|
104
|
+
# Attribute the violation to the page it happened on, not to this
|
|
105
|
+
# /dispatch/reports endpoint the browser POSTed the report to — otherwise
|
|
106
|
+
# every CSP violation across the app collapses onto one bogus URL.
|
|
107
|
+
request_url: fields[:document_uri],
|
|
108
|
+
tags: { csp: "true", report_type: "csp-violation" }.merge(fields)
|
|
109
|
+
}
|
|
104
110
|
)
|
|
105
111
|
end
|
|
106
112
|
|