Skip to content

pass custom container to mount mfe app#218

Open
aquibbaig wants to merge 3 commits into
coderabbit_micro_frontendfrom
feat/grafana-shadow-dom
Open

pass custom container to mount mfe app#218
aquibbaig wants to merge 3 commits into
coderabbit_micro_frontendfrom
feat/grafana-shadow-dom

Conversation

@aquibbaig

@aquibbaig aquibbaig commented Jun 1, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • MFE can mount into Document, DocumentFragment, or HTMLElement containers.
    • Theme assets and runtime styles (Emotion) are scoped and injected into the chosen container.
    • Portals now render into a Shadow DOM container and can pass that container to children.
  • Refactor

    • Mount/update/unmount lifecycle uses the provided container to load, move, and clean up theme and style assets.
  • Other

    • Micro-frontend HTML updated to reference hashed theme CSS assets.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an exported MfeContainer type, rewrites portal rendering to use ShadowRoot, scopes theme stylesheet and Emotion style tag handling to a container-specific root, updates lifecycle hooks to set/reset that root, and makes FnAppProvider create/use a container-scoped Emotion cache.

Changes

Container-Scoped Theme Management

Layer / File(s) Summary
Type contract and Shadow-root portal
public/app/fn-app/types.ts, public/app/fn-app/utils.tsx
Introduce MfeContainer and update RenderPortal so it creates/uses an open shadow root and accepts children as `ReactNode
Container state and helpers
public/app/fn-app/create-mfe.ts
Add static themeStyleRoot, import Emotion cache, add helpers to set/get the root, compute append target (Document.body vs element), collect scoped <link> elements, and move Emotion sheet tags into the computed target.
Scoped theme operations & lifecycle wiring
public/app/fn-app/create-mfe.ts
Refactor removeThemeLinks to operate on scoped roots, extend loadFnTheme(styleRoot?) to remove and append theme <link>s inside the scoped root, and update mount/update/unmount to set/reset the root and move Emotion styles before theme load.
FnAppProvider: Emotion cache per container
public/app/fn-app/fn-app-provider.tsx
Add optional container prop, compute the target container, reparent Emotion stylesheet tags, create a grafana-mf Emotion cache with createCache and wrap the app tree with CacheProvider.
Dashboard portal -> provider container propagation
public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
Render dashboards via RenderPortal’s container callback and pass the portal container into FnAppProvider (container={container}) so provider and createMfe share the same scoped root.
Microfrontend static assets
public/microfrontends/fn_dashboard/index.html
Update window.fnData.themePaths CSS hashes and simplify JS bootstrapping scripts to two bundles.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 In a burrow made of DOM and light,
Styles hop home and settle tight,
Emotion sheets find tidy beds,
Links live where their container spreads,
A scoped little hop—styles just right.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: enabling custom container support for mounting the MFE (micro-frontend) app, which is the core change throughout the affected files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/grafana-shadow-dom
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/grafana-shadow-dom

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/app/fn-app/create-mfe.ts`:
- Around line 105-116: The static theme root can be left unset or stale causing
getThemeStyleRoot() to throw or reuse a previous instance; update
setThemeStyleRoot(container) so when container is falsy it resets or defaults
createMfe.themeStyleRoot to a safe value (e.g., document) and/or ensure
mountFnApp() and updateFnApp() call loadFnTheme() with a reinitialized style
root; specifically, modify setThemeStyleRoot to set createMfe.themeStyleRoot =
container || document (or null-clearing before load) and ensure
getThemeStyleRoot() no longer throws by relying on that default, touching the
setThemeStyleRoot, getThemeStyleRoot, mountFnApp, updateFnApp, and loadFnTheme
references.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 8ac32184-e883-4f46-8c3a-34282347845c

📥 Commits

Reviewing files that changed from the base of the PR and between 80bbd63 and 2b7da9b.

📒 Files selected for processing (2)
  • public/app/fn-app/create-mfe.ts
  • public/app/fn-app/types.ts
📜 Review details
⏰ 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). (1)
  • GitHub Check: build-and-test
🧰 Additional context used
📓 Path-based instructions (1)
**/*.ts

⚙️ CodeRabbit configuration file

**/*.ts: - Do not allow use of eslint-disable, @ts-expect-error, or @ts-ignore unless there's a clear, inline comment explaining why it's necessary.

  • Suggest early returns in place of nested if, else or loops with complex branching.
  • Flag function-wide scopes created by try / catch or top-level if / else. Recommend moving the inner logic to its own function.
  • Flag use of try / catch for control flow. Recommend using .catch() with appropriate error handling.
  • Flag try / catch that introduces a let where .catch() with const could be used instead.
  • Flag catch blocks that narrow the caught error to Error. Suggest typing the catch parameter as unknown.
  • Flag cases where types are narrowed manually before passing a value to the logger. Suggest passing the value directly without narrowing.
  • Flag logging expressions that extract error.message or convert the error to a string. Suggest logging the full error value instead.
  • Flag variables created from error.message or String(error) that are then logged. Suggest logging the original error value directly.
  • When let is used to accumulate a value through conditions, suggest replacing it with a function that returns the final value directly.
  • Flag let followed by a single conditional reassignment. Suggest a const with a ternary or a helper that returns the final value.
  • Flag Map get-or-create patterns that assign to a let (e.g. let x = map.get(k); if (!x) { x = new ...; map.set(k, x) }). Suggest a getOrCreate* helper that returns the value and keeps the variable const.
  • Flag nested conditions that can be combined into a single guard. Suggest simplifying with an early return.
  • When encountering side effects such as mutation in forEach, suggest replacing with map, filter or reduce.
  • Recommend introducing intermediate variables when string interpolation contains non-trivial logic.
  • Ban all as type assertions everywhere, including chains like ...

Files:

  • public/app/fn-app/types.ts
  • public/app/fn-app/create-mfe.ts

Comment thread public/app/fn-app/create-mfe.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/app/fn-app/fn-app-provider.tsx`:
- Around line 30-48: The code currently mutates the process-wide emotionCssCache
and moves its tags inside the useMemo callback for the FnAppProvider, which
breaks multi-dashboard rendering; keep the per-container createCache call inside
useMemo (createCache with key 'grafana-mf' and container) but remove any
reassignment of emotionCssCache.sheet.container and DOM appendChild calls from
the memo callback, and instead perform any required global-sheet relocation in a
useEffect (or avoid relocating shared `@emotion/css` entirely) so side effects run
only after mount; reference emotionCssCache, useMemo, createCache, FnAppProvider
and DashboardPortal when making the change.

In `@public/microfrontends/fn_dashboard/index.html`:
- Line 10: The inline <script> tag currently has an empty nonce attribute
(nonce="") which nullifies CSP protection; either remove the nonce attribute
entirely if you are not using CSP, or populate it with a server-generated
cryptographically secure nonce value and inject that same value into the
server's Content-Security-Policy header (matching 'nonce-{value}'); locate the
inline script tag with nonce="" in the HTML (the <script nonce=""> element) and
implement server-side generation/injection of the nonce or remove the attribute
accordingly so the CSP remains effective.
- Around line 20-22: The two script tags referencing
runtime~fn_dashboard.a5d060ef4f8cb38ef003.js and
fn_dashboard.61924fc7a748b4f2ecc3.js currently have empty nonce attributes and
no SRI/CORS attributes; fix by removing empty nonce="" or (preferably) replacing
with a server-generated cryptographically-random nonce value that matches the
page CSP, and restore Subresource Integrity by computing and adding
integrity="sha384-..." (or sha512) for each script file and add
crossorigin="anonymous" so SRI can function; locate the tags in
public/microfrontends/fn_dashboard/index.html and update the script elements
referencing those two filenames accordingly.
- Around line 20-22: In public/microfrontends/fn_dashboard/index.html the script
tags reference non-existent hashed bundles
runtime~fn_dashboard.a5d060ef4f8cb38ef003.js and
fn_dashboard.61924fc7a748b4f2ecc3.js; either update those src attributes to
point to the actual built filenames/paths produced by your bundler (replace the
two referenced filenames with the real outputs) or ensure your build/pipeline
emits those specific files into the public/build location so the paths resolve
at runtime (adjust your build config or deployment static asset step to
produce/serve the expected bundles).
- Around line 13-14: The template references hashed assets under public/build
(e.g. the strings 'public/build/grafana.light.<hash>.css',
'public/build/grafana.dark.<hash>.css' and runtime~fn_dashboard.<hash>.js /
fn_dashboard.<hash>.js) which are missing; either update the asset URLs in the
index.html to the actual deployed locations (e.g. remove the leading "public/"
so they resolve to /build/...) or make the build/deploy process copy/generate
those hashed files into public/build; locate the asset declarations in the
fn_dashboard index.html (the base href and the light/dark CSS and JS script
references) and change them to point to the correct runtime paths or to template
placeholders that your bundler will replace during build.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: b145b256-f5d3-4952-89cc-906c013ee4ac

📥 Commits

Reviewing files that changed from the base of the PR and between 3652015 and fc60fff.

📒 Files selected for processing (5)
  • public/app/fn-app/create-mfe.ts
  • public/app/fn-app/fn-app-provider.tsx
  • public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
  • public/app/fn-app/utils.tsx
  • public/microfrontends/fn_dashboard/index.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • public/app/fn-app/create-mfe.ts
📜 Review details
⏰ 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). (2)
  • GitHub Check: build_and_deploy
  • GitHub Check: build-and-test
🔇 Additional comments (4)
public/microfrontends/fn_dashboard/index.html (2)

1-7: LGTM!


8-9: LGTM!

public/app/fn-app/utils.tsx (1)

8-22: LGTM!

public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx (1)

74-87: ⚡ Quick win

Verify container prop type compatibility.

Type is compatible: FNDashboardProps['container'] is MfeContainer | null (MfeContainer = Document | DocumentFragment | HTMLElement), and RenderPortal’s render-prop callback parameter is typed as MfeContainer while it passes shadowRoot from attachShadow (DOM ShadowRoot extends DocumentFragment).

Comment on lines +30 to +48
const emotionCache = useMemo(
() => {
const container =
props.container instanceof Document ? props.container.head : props.container || document.head;

emotionCssCache.sheet.container = container;
emotionCssCache.sheet.tags.forEach((tag) => {
if (tag.parentNode !== container) {
container.appendChild(tag);
}
});

return createCache({
key: 'grafana-mf',
container,
});
},
[props.container]
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Mutating the shared global Emotion cache inside useMemo breaks multi-dashboard rendering.

emotionCssCache is the process-wide singleton from @emotion/css shared by the host app and every dashboard. DashboardPortal renders one FnAppProvider per dashboard, each with its own shadow-root container. Two problems:

  1. Each instance reassigns emotionCssCache.sheet.container and appendChild's all existing @emotion/css tags into its own shadow root. With more than one dashboard the last render wins, relocating those style tags out of the host (and out of every other dashboard's root), so other consumers lose their styles.
  2. These are side effects (global mutation + DOM moves) executed inside useMemo, which must be pure. Under concurrent rendering React may invoke or discard the memo callback speculatively, making the relocation unreliable.

Keep createCache in useMemo for the per-container scoping, but move the global-sheet relocation into an effect, and reconsider whether the shared @emotion/css cache should be re-parented at all when multiple shadow roots are active.

♻️ Suggested direction
-  const emotionCache = useMemo(
-    () => {
-      const container =
-        props.container instanceof Document ? props.container.head : props.container || document.head;
-
-      emotionCssCache.sheet.container = container;
-      emotionCssCache.sheet.tags.forEach((tag) => {
-        if (tag.parentNode !== container) {
-          container.appendChild(tag);
-        }
-      });
-
-      return createCache({
-        key: 'grafana-mf',
-        container,
-      });
-    },
-    [props.container]
-  );
+  const container = useMemo(
+    () => (props.container instanceof Document ? props.container.head : props.container || document.head),
+    [props.container]
+  );
+  const emotionCache = useMemo(() => createCache({ key: 'grafana-mf', container }), [container]);
+
+  useEffect(() => {
+    // Re-parent existing `@emotion/css` tags into the scoped container.
+    // NOTE: this still touches a shared global cache; verify it is safe when
+    // multiple dashboards (shadow roots) are mounted simultaneously.
+    emotionCssCache.sheet.container = container;
+    emotionCssCache.sheet.tags.forEach((tag) => {
+      if (tag.parentNode !== container) {
+        container.appendChild(tag);
+      }
+    });
+  }, [container]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const emotionCache = useMemo(
() => {
const container =
props.container instanceof Document ? props.container.head : props.container || document.head;
emotionCssCache.sheet.container = container;
emotionCssCache.sheet.tags.forEach((tag) => {
if (tag.parentNode !== container) {
container.appendChild(tag);
}
});
return createCache({
key: 'grafana-mf',
container,
});
},
[props.container]
);
const container = useMemo(
() => (props.container instanceof Document ? props.container.head : props.container || document.head),
[props.container]
);
const emotionCache = useMemo(() => createCache({ key: 'grafana-mf', container }), [container]);
useEffect(() => {
// Re-parent existing `@emotion/css` tags into the scoped container.
// NOTE: this still touches a shared global cache; verify it is safe when
// multiple dashboards (shadow roots) are mounted simultaneously.
emotionCssCache.sheet.container = container;
emotionCssCache.sheet.tags.forEach((tag) => {
if (tag.parentNode !== container) {
container.appendChild(tag);
}
});
}, [container]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/app/fn-app/fn-app-provider.tsx` around lines 30 - 48, The code
currently mutates the process-wide emotionCssCache and moves its tags inside the
useMemo callback for the FnAppProvider, which breaks multi-dashboard rendering;
keep the per-container createCache call inside useMemo (createCache with key
'grafana-mf' and container) but remove any reassignment of
emotionCssCache.sheet.container and DOM appendChild calls from the memo
callback, and instead perform any required global-sheet relocation in a
useEffect (or avoid relocating shared `@emotion/css` entirely) so side effects run
only after mount; reference emotionCssCache, useMemo, createCache, FnAppProvider
and DashboardPortal when making the change.


<body class="theme-light app-grafana">
<div id="grafanaRoot"></div>
<script nonce="">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Empty nonce attribute defeats CSP protection.

The nonce="" attribute on this inline script tag has an empty value, which defeats Content Security Policy (CSP) protection. If CSP is enabled with script-src 'nonce-{value}', an empty nonce effectively allows any inline script to execute, creating a security vulnerability.

Either populate the nonce with a cryptographically random value generated server-side, or remove the attribute if CSP is not being used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/microfrontends/fn_dashboard/index.html` at line 10, The inline
<script> tag currently has an empty nonce attribute (nonce="") which nullifies
CSP protection; either remove the nonce attribute entirely if you are not using
CSP, or populate it with a server-generated cryptographically secure nonce value
and inject that same value into the server's Content-Security-Policy header
(matching 'nonce-{value}'); locate the inline script tag with nonce="" in the
HTML (the <script nonce=""> element) and implement server-side
generation/injection of the nonce or remove the attribute accordingly so the CSP
remains effective.

Comment on lines +13 to +14
light: '../../../public/build/grafana.light.65904cef23d9f7f76c4a.css',
dark: '../../../public/build/grafana.dark.7c4d93ecbdd92098eb9a.css',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if the hashed CSS files referenced in the HTML exist

# Expected CSS files based on the HTML
LIGHT_CSS="public/build/grafana.light.65904cef23d9f7f76c4a.css"
DARK_CSS="public/build/grafana.dark.7c4d93ecbdd92098eb9a.css"

echo "Checking for CSS files..."
if [ -f "$LIGHT_CSS" ]; then
  echo "✓ Found: $LIGHT_CSS"
else
  echo "✗ Missing: $LIGHT_CSS"
fi

if [ -f "$DARK_CSS" ]; then
  echo "✓ Found: $DARK_CSS"
else
  echo "✗ Missing: $DARK_CSS"
fi

# Also search for any grafana theme CSS files to see what actually exists
echo -e "\nAll Grafana theme CSS files in public/build/:"
fd -e css 'grafana\.(light|dark)\.' public/build/ --exec echo "{}"

Repository: coderabbitai/grafana

Length of output: 381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Inspect index.html base + themePaths snippet =="
nl -ba public/microfrontends/fn_dashboard/index.html | sed -n '1,80p'

echo
echo "== Locate referenced hashed CSS files anywhere in repo =="
LIGHT_REF="grafana.light.65904cef23d9f7f76c4a.css"
DARK_REF="grafana.dark.7c4d93ecbdd92098eb9a.css"

echo "Searching for: $LIGHT_REF"
rg -n --hidden --no-ignore -S "$LIGHT_REF" . || true
echo "Searching for: $DARK_REF"
rg -n --hidden --no-ignore -S "$DARK_REF" . || true

echo
echo "== List any grafana.light.* and grafana.dark.* CSS files in repo =="
fd -e css "grafana\.(light|dark)\..*" . || true

echo
echo "== Check for common build output dirs (existence only) =="
for d in public/build build public/dist public/assets dist assets; do
  if [ -d "$d" ]; then echo "Found dir: $d"; else echo "Missing dir: $d"; fi
done

Repository: coderabbitai/grafana

Length of output: 176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Inspect index.html base + themePaths snippet =="
awk '{printf "%5d  %s\n", NR, $0}' public/microfrontends/fn_dashboard/index.html | sed -n '1,120p'

echo
echo "== Locate referenced hashed CSS files anywhere in repo =="
LIGHT_REF="grafana.light.65904cef23d9f7f76c4a.css"
DARK_REF="grafana.dark.7c4d93ecbdd92098eb9a.css"

echo "Searching for: $LIGHT_REF"
rg -n --hidden --no-ignore -S "$LIGHT_REF" . || true
echo "Searching for: $DARK_REF"
rg -n --hidden --no-ignore -S "$DARK_REF" . || true

echo
echo "== List any grafana.light.* and grafana.dark.* CSS files in repo =="
fd -e css "grafana\.(light|dark)\..*" . || true

echo
echo "== Check for common build output dirs (existence only) =="
for d in public/build public/dist public/assets build dist assets; do
  if [ -d "$d" ]; then echo "Found dir: $d"; else echo "Missing dir: $d"; fi
done

Repository: coderabbitai/grafana

Length of output: 1680


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check whether referenced build JS files exist =="
RUNTIME="public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js"
BUNDLE="public/build/fn_dashboard.61924fc7a748b4f2ecc3.js"

for f in "$RUNTIME" "$BUNDLE"; do
  if [ -f "$f" ]; then
    echo "✓ Found: $f"
  else
    echo "✗ Missing: $f"
  fi
done

Repository: coderabbitai/grafana

Length of output: 246


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden --no-ignore -S "runtime~fn_dashboard\.a5d060ef4f8cb38ef003\.js" . || true
rg -n --hidden --no-ignore -S "fn_dashboard\.61924fc7a748b4f2ecc3\.js" . || true
rg -n --hidden --no-ignore -S "grafana\.(light|dark)\." public/microfrontends/fn_dashboard/index.html || true
ls -la public || true
ls -la public/microfrontends/fn_dashboard || true

Repository: coderabbitai/grafana

Length of output: 2017


Fix asset URLs: referenced hashed CSS/JS under public/build/ are missing in this repo snapshot

public/microfrontends/fn_dashboard/index.html resolves (with <base href="/" />) to /public/build/grafana.{light|dark}.<hash>.css and also loads /public/build/runtime~fn_dashboard.<hash>.js + /public/build/fn_dashboard.<hash>.js, but public/build/ (and those hashed files) aren’t present here—these requests will 404 unless your build/deploy step generates/copies them into public/build (or the template should be updated to the real asset locations).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/microfrontends/fn_dashboard/index.html` around lines 13 - 14, The
template references hashed assets under public/build (e.g. the strings
'public/build/grafana.light.<hash>.css', 'public/build/grafana.dark.<hash>.css'
and runtime~fn_dashboard.<hash>.js / fn_dashboard.<hash>.js) which are missing;
either update the asset URLs in the index.html to the actual deployed locations
(e.g. remove the leading "public/" so they resolve to /build/...) or make the
build/deploy process copy/generate those hashed files into public/build; locate
the asset declarations in the fn_dashboard index.html (the base href and the
light/dark CSS and JS script references) and change them to point to the correct
runtime paths or to template placeholders that your bundler will replace during
build.

Comment on lines +20 to +22
<script nonce="" src="../../../public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js" type="text/javascript"></script>

<script nonce="" src="../../../public/build/fn_dashboard.61924fc7a748b4f2ecc3.js" type="text/javascript"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Empty nonce attributes and missing integrity protection.

These script tags have two security issues:

  1. Empty nonce attributes (nonce=""): Same CSP bypass vulnerability as noted earlier. Each script with an empty nonce defeats Content Security Policy protection.

  2. Missing Subresource Integrity (SRI): The AI summary confirms that integrity and crossorigin attributes were removed. This is a security regression that eliminates protection against script tampering. SRI ensures that loaded scripts haven't been modified on the CDN or during transit.

Recommended fixes:

  • Populate nonce attributes with server-generated cryptographically random values, or remove if CSP is not used
  • Restore integrity attributes with SHA-384/SHA-512 hashes of the script files
  • Add crossorigin="anonymous" to enable SRI with CORS
🔒 Example with SRI attributes
-    <script nonce="" src="../../../public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js" type="text/javascript"></script>
+    <script nonce="{SERVER_GENERATED_NONCE}" src="../../../public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js" type="text/javascript" integrity="sha384-{HASH}" crossorigin="anonymous"></script>
       
-    <script nonce="" src="../../../public/build/fn_dashboard.61924fc7a748b4f2ecc3.js" type="text/javascript"></script>
+    <script nonce="{SERVER_GENERATED_NONCE}" src="../../../public/build/fn_dashboard.61924fc7a748b4f2ecc3.js" type="text/javascript" integrity="sha384-{HASH}" crossorigin="anonymous"></script>

Note: Generate integrity hashes using: openssl dgst -sha384 -binary <file> | openssl base64 -A

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/microfrontends/fn_dashboard/index.html` around lines 20 - 22, The two
script tags referencing runtime~fn_dashboard.a5d060ef4f8cb38ef003.js and
fn_dashboard.61924fc7a748b4f2ecc3.js currently have empty nonce attributes and
no SRI/CORS attributes; fix by removing empty nonce="" or (preferably) replacing
with a server-generated cryptographically-random nonce value that matches the
page CSP, and restore Subresource Integrity by computing and adding
integrity="sha384-..." (or sha512) for each script file and add
crossorigin="anonymous" so SRI can function; locate the tags in
public/microfrontends/fn_dashboard/index.html and update the script elements
referencing those two filenames accordingly.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if the hashed JS bundles referenced in the HTML exist

# Expected JS files based on the HTML
RUNTIME_JS="public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js"
MAIN_JS="public/build/fn_dashboard.61924fc7a748b4f2ecc3.js"

echo "Checking for JS bundles..."
if [ -f "$RUNTIME_JS" ]; then
  echo "✓ Found: $RUNTIME_JS"
else
  echo "✗ Missing: $RUNTIME_JS"
fi

if [ -f "$MAIN_JS" ]; then
  echo "✓ Found: $MAIN_JS"
else
  echo "✗ Missing: $MAIN_JS"
fi

# Also search for any fn_dashboard JS files to see what actually exists
echo -e "\nAll fn_dashboard JS files in public/build/:"
fd -e js 'fn_dashboard' public/build/ --exec echo "{}"

Repository: coderabbitai/grafana

Length of output: 385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate the expected hashed bundle filenames anywhere in the repo
echo "Searching for exact hashed bundle filenames..."
rg -n --hidden --no-ignore-vcs -S "runtime~fn_dashboard\.a5d060ef4f8cb38ef003\.js|fn_dashboard\.61924fc7a748b4f2ecc3\.js" . || true

echo
echo "Finding files by name (may take a moment)..."
find . -type f \( -name 'runtime~fn_dashboard.a5d060ef4f8cb38ef003.js' -o -name 'fn_dashboard.61924fc7a748b4f2ecc3.js' \) -print || true

echo
# 2) Check whether a public/build directory exists anywhere under repo
echo "Checking for build output directories..."
find . -type d -path "*/public/build" -print || true
find . -type d -name build -maxdepth 4 -print || true

echo
# 3) Show the relevant section of the HTML for base href and script tags
echo "---- public/microfrontends/fn_dashboard/index.html (relevant section) ----"
nl -ba public/microfrontends/fn_dashboard/index.html | sed -n '1,120p'

Repository: coderabbitai/grafana

Length of output: 713


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Does any fn_dashboard JS exist anywhere?"
find . -type f \( -name '*fn_dashboard*js' -o -name '*runtime~fn_dashboard*js' \) -print || true

echo
echo "2) What JS files exist under pkg/build and scripts/build (limited by fn_dashboard keywords)?"
for d in ./pkg/build ./scripts/build; do
  if [ -d "$d" ]; then
    echo "-- $d --"
    find "$d" -type f -name '*fn_dashboard*js' -print || true
    find "$d" -type f -name '*runtime*fn_dashboard*js' -print || true
  fi
done

echo
echo "3) Reprint public/microfrontends/fn_dashboard/index.html lines 1-60 (no nl)."
sed -n '1,80p' public/microfrontends/fn_dashboard/index.html

Repository: coderabbitai/grafana

Length of output: 1027


Fix: referenced hashed fn_dashboard JS bundles aren’t present at /public/build

  • In public/microfrontends/fn_dashboard/index.html (lines 20-22), the page loads:
    • ../../../public/build/runtime~fn_dashboard.a5d060ef4f8cb38ef003.js
    • ../../../public/build/fn_dashboard.61924fc7a748b4f2ecc3.js
  • The repo checkout has no public/build directory and these exact hashed JS files don’t exist anywhere in the repository, so the script tags will fail (404) unless your build/deploy pipeline generates/serves them at /public/build.
  • Update the hashes/paths to match the actual build outputs or ensure the bundles are produced and available at the expected location at runtime.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/microfrontends/fn_dashboard/index.html` around lines 20 - 22, In
public/microfrontends/fn_dashboard/index.html the script tags reference
non-existent hashed bundles runtime~fn_dashboard.a5d060ef4f8cb38ef003.js and
fn_dashboard.61924fc7a748b4f2ecc3.js; either update those src attributes to
point to the actual built filenames/paths produced by your bundler (replace the
two referenced filenames with the real outputs) or ensure your build/pipeline
emits those specific files into the public/build location so the paths resolve
at runtime (adjust your build config or deployment static asset step to
produce/serve the expected bundles).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant