Skip to content

Conversation

@wilsonrivera
Copy link
Contributor

@wilsonrivera wilsonrivera commented Nov 10, 2025

Summary by CodeRabbit

  • New Features

    • Improved Google Tag Manager handling: GTM scripts and consent blocks now render only when an ID is provided, reducing unnecessary output and improving initialization flow.
  • Bug Fixes

    • Analytics and advertising services now only load in production environments, preventing tracking during development.
  • Chores

    • Content Security Policy updated to allow additional analytics/tracking domains in production.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@coderabbitai
Copy link

coderabbitai bot commented Nov 10, 2025

Walkthrough

Make Google Tag Manager components props-driven (accepting a gtmId), replace Next.js Script usage with raw script tags for GTM, conditionally render GTM markup when gtmId is present, and adjust next.config.mjs CSP to always allow googletagmanager in frame-src and to include several analytics script-src domains only in production.

Changes

Cohort / File(s) Summary
CSP and Production Configuration
studio/next.config.mjs
Added isProduction constant; extended Content-Security-Policy frame-src to always include https://www.googletagmanager.com; made several script-src analytics domains conditional on production (append GTM, LinkedIn, Osano, DoubleClick, and Microsoft Clarity domains only when isProduction is true).
Analytics Component Props & Markup
studio/src/components/layout/analytics/gtm-script.tsx
Introduced exported GtmScriptProps with `gtmId: string
Document Template Integration
studio/src/pages/_document.tsx
Render GTM inline initialization and the GtmScript together under the same gtmId conditional; updated usages to pass gtmId to both GtmScript and GtmNoScript.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Verify correct conditional inclusion of CSP script-src domains and that frame-src change matches intended policy.
  • Validate gtmId prop flow and conditional rendering in _document.tsx.
  • Review dangerouslySetInnerHTML contents for correctness and trusted source usage.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: enabling GTM (Google Tag Manager) script functionality across the codebase through configuration updates and component modifications.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
studio/next.config.mjs (2)

58-60: Consider making frame-src GTM source conditional on production.

The frame-src directive includes https://www.googletagmanager.com unconditionally, but script-src only allows GTM sources in production (lines 70-80). This inconsistency means GTM frames are permitted in all environments even though GTM scripts won't load outside production.

Apply this diff to align frame-src with the production-only approach:

-  frame-src 'self' https://js.stripe.com https://hooks.stripe.com https://www.googletagmanager.com ${
-    isPreview ? "https://vercel.live/ https://vercel.com" : ""
+  frame-src 'self' https://js.stripe.com https://hooks.stripe.com ${
+    isProduction ? "https://www.googletagmanager.com" : ""
+  } ${isPreview ? "https://vercel.live/ https://vercel.com" : ""
   };

65-65: Consider making analytics image sources production-only.

The img-src directive includes *.ads.linkedin.com and *.google.com unconditionally, which are analytics/advertising domains. For consistency with the script-src approach (lines 70-80), these should likely be restricted to production only.

Apply this diff to make img-src analytics sources conditional:

   img-src 'self'${
     isPreview
       ? " https://vercel.live/ https://vercel.com *.pusher.com/ data: blob:"
       : ""
-  } *.ads.linkedin.com *.google.com;
+  }${isProduction ? " *.ads.linkedin.com *.google.com" : ""};
studio/src/components/layout/analytics/gtm-script.tsx (1)

3-5: Optional: Remove trailing semicolon after interface.

TypeScript interfaces typically don't require a semicolon after the closing brace. While not incorrect, removing it aligns with common conventions.

Apply this diff:

 export interface GtmScriptProps {
   gtmId: string | undefined;
-};
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8399eb8 and 5061333.

📒 Files selected for processing (3)
  • studio/next.config.mjs (2 hunks)
  • studio/src/components/layout/analytics/gtm-script.tsx (3 hunks)
  • studio/src/pages/_document.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
studio/src/pages/_document.tsx (2)
studio/src/components/layout/analytics/gtm-script.tsx (2)
  • GtmScript (7-63)
  • GtmNoScript (65-80)
studio/next.config.mjs (1)
  • isProduction (6-6)
🪛 ast-grep (0.39.7)
studio/src/components/layout/analytics/gtm-script.tsx

[warning] 15-15: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)


[warning] 30-30: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
studio/src/components/layout/analytics/gtm-script.tsx

[error] 16-16: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 31-31: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
🔇 Additional comments (8)
studio/next.config.mjs (2)

6-6: LGTM!

The isProduction constant is correctly defined and provides a clear way to conditionally enable analytics integrations.


70-80: LGTM!

The conditional inclusion of analytics vendor sources in script-src for production environments is well-implemented and aligns with the PR's objective to enable GTM in production.

studio/src/components/layout/analytics/gtm-script.tsx (4)

7-10: LGTM!

The guard clause properly prevents rendering GTM scripts when gtmId is not provided, making the component safe to use unconditionally.


14-27: dangerouslySetInnerHTML usage is safe here.

The static analysis warnings about dangerouslySetInnerHTML are false positives in this context. The gtmId is properly escaped via JSON.stringify() (line 24), and the source is a controlled environment variable. The GTM initialization code is static and safe.

However, note that switching from Next.js <Script> component to raw <script> tags changes the loading behavior—these scripts now execute synchronously during HTML parsing rather than using Next.js's script optimization strategies.


29-60: LGTM!

The Osano consent integration properly handles both synchronous (already loaded) and asynchronous (not yet loaded) scenarios. The dangerouslySetInnerHTML usage here contains only static JavaScript with no dynamic content interpolation, making it safe.


65-80: LGTM!

The GtmNoScript component correctly accepts gtmId as a prop and safely interpolates it into the iframe URL using a template literal. The guard clause maintains consistency with GtmScript.

studio/src/pages/_document.tsx (2)

38-50: LGTM!

The GTM initialization is correctly structured with the default consent configuration loaded before the GTM script. Passing gtmId as a prop aligns with the refactored component interface, and the production-only rendering is appropriate.


133-133: LGTM!

The GtmNoScript component correctly receives the gtmId prop and is positioned at the top of the body, following Google Tag Manager's recommended implementation pattern.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
studio/src/components/layout/analytics/gtm-script.tsx (1)

1-3: Optional: Remove semicolon after interface for TypeScript convention.

The semicolon on line 3 after the closing brace is valid but unconventional. Most TypeScript style guides omit it for interface and type declarations.

Apply this diff:

 export interface GtmScriptProps {
   gtmId: string | undefined;
-};
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5061333 and 1a087f7.

📒 Files selected for processing (1)
  • studio/src/components/layout/analytics/gtm-script.tsx (3 hunks)
🧰 Additional context used
🪛 ast-grep (0.39.7)
studio/src/components/layout/analytics/gtm-script.tsx

[warning] 13-13: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)


[warning] 28-28: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
studio/src/components/layout/analytics/gtm-script.tsx

[error] 14-14: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 29-29: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
studio/src/components/layout/analytics/gtm-script.tsx (2)

5-61: Implementation is correct; static analysis warnings are false positives.

The use of dangerouslySetInnerHTML on lines 14 and 29 is flagged by static analysis tools, but this is a legitimate and safe use case:

  1. All GTM script content is hardcoded and controlled by developers
  2. The only dynamic value (gtmId) is properly escaped using JSON.stringify() on line 22
  3. This follows the standard pattern for integrating third-party analytics scripts per GTM documentation

The conditional rendering (lines 6-8) and the overall implementation are well-structured.


63-78: LGTM!

The GtmNoScript implementation correctly handles the conditional rendering and follows GTM documentation for the noscript fallback. The template literal safely injects the gtmId into the iframe URL.

@wilsonrivera wilsonrivera merged commit 1bdc79c into main Nov 10, 2025
10 checks passed
@wilsonrivera wilsonrivera deleted the wilson/eng-8446-persist-ad-url-params-and-cookies-during-cosmo-sign-up-in2 branch November 10, 2025 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants