Implement Sentry JavaScript integration with proper CSP configuration
- Restored Sentry JavaScript SDK script tag - Created custom ContentSecurityPolicy plug to allow Sentry domains - Added proper CSP headers allowing: - js.sentry-cdn.com for the SDK - *.ingest.sentry.io and *.sentry.io for error reporting - Initialized Sentry in app.js with: - Proper DSN configuration - Browser tracing integration - 10% transaction sampling rate - Filter for non-critical ResizeObserver errors - Added CSP plug to router pipeline after put_secure_browser_headers This properly integrates Sentry for JavaScript error tracking while maintaining security through explicit CSP configuration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
df436ab55d
commit
dade93ee5c
4 changed files with 60 additions and 0 deletions
|
|
@ -22,6 +22,25 @@ import { Socket } from "phoenix";
|
||||||
import { LiveSocket } from "phoenix_live_view";
|
import { LiveSocket } from "phoenix_live_view";
|
||||||
import topbar from "../vendor/topbar";
|
import topbar from "../vendor/topbar";
|
||||||
|
|
||||||
|
// Initialize Sentry for JavaScript error tracking
|
||||||
|
if (typeof window.Sentry !== 'undefined') {
|
||||||
|
window.Sentry.init({
|
||||||
|
dsn: "https://337ece4c07ff53c6719d900adfddd6e4@o4509627566063616.ingest.us.sentry.io/4509691336785920",
|
||||||
|
environment: process.env.NODE_ENV || "production",
|
||||||
|
integrations: [
|
||||||
|
new window.Sentry.BrowserTracing(),
|
||||||
|
],
|
||||||
|
tracesSampleRate: 0.1, // Capture 10% of transactions for performance monitoring
|
||||||
|
beforeSend(event, hint) {
|
||||||
|
// Filter out known non-critical errors
|
||||||
|
if (hint.originalException?.message?.includes('ResizeObserver loop limit exceeded')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let csrfToken = document.querySelector("meta[name='csrf-token']")?.getAttribute("content") || "";
|
let csrfToken = document.querySelector("meta[name='csrf-token']")?.getAttribute("content") || "";
|
||||||
if (!csrfToken) {
|
if (!csrfToken) {
|
||||||
console.error("CSRF token not found in meta tags");
|
console.error("CSRF token not found in meta tags");
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@
|
||||||
</script>
|
</script>
|
||||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
||||||
</script>
|
</script>
|
||||||
|
<script src="https://js.sentry-cdn.com/be4b53768e7c243cc72fa78ee7b7ec8c.min.js" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class={body_class(assigns)}>
|
<body class={body_class(assigns)}>
|
||||||
{@inner_content}
|
{@inner_content}
|
||||||
|
|
|
||||||
38
lib/aprsme_web/plugs/content_security_policy.ex
Normal file
38
lib/aprsme_web/plugs/content_security_policy.ex
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
defmodule AprsmeWeb.Plugs.ContentSecurityPolicy do
|
||||||
|
@moduledoc """
|
||||||
|
Sets Content Security Policy headers with Sentry integration support.
|
||||||
|
"""
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
put_resp_header(conn, "content-security-policy", csp_policy())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp csp_policy do
|
||||||
|
Enum.join(
|
||||||
|
[
|
||||||
|
"default-src 'self'",
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.sentry-cdn.com https://unpkg.com https://cdn.jsdelivr.net",
|
||||||
|
"style-src 'self' 'unsafe-inline' https://unpkg.com",
|
||||||
|
"img-src 'self' data: https: blob:",
|
||||||
|
"font-src 'self' data:",
|
||||||
|
"connect-src 'self' wss://#{host()} https://*.ingest.sentry.io https://*.sentry.io https://nominatim.openstreetmap.org https://tile.openstreetmap.org https://*.tile.openstreetmap.org",
|
||||||
|
"media-src 'self'",
|
||||||
|
"object-src 'none'",
|
||||||
|
"frame-ancestors 'none'",
|
||||||
|
"base-uri 'self'",
|
||||||
|
"form-action 'self'",
|
||||||
|
"frame-src 'self'",
|
||||||
|
"manifest-src 'self'",
|
||||||
|
"worker-src 'self' blob:"
|
||||||
|
],
|
||||||
|
"; "
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp host do
|
||||||
|
Application.get_env(:aprsme, AprsmeWeb.Endpoint)[:url][:host] || "localhost"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -15,6 +15,7 @@ defmodule AprsmeWeb.Router do
|
||||||
plug :put_root_layout, {AprsmeWeb.Layouts, :root}
|
plug :put_root_layout, {AprsmeWeb.Layouts, :root}
|
||||||
plug :protect_from_forgery
|
plug :protect_from_forgery
|
||||||
plug :put_secure_browser_headers
|
plug :put_secure_browser_headers
|
||||||
|
plug AprsmeWeb.Plugs.ContentSecurityPolicy
|
||||||
plug :fetch_current_user
|
plug :fetch_current_user
|
||||||
plug AprsmeWeb.Plugs.SetLocale
|
plug AprsmeWeb.Plugs.SetLocale
|
||||||
plug IPGeolocation
|
plug IPGeolocation
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue