-
Notifications
You must be signed in to change notification settings - Fork 211
fix: Metabase embed on /careers showing "Message seems corrupt or manipulated" #2829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,35 @@ | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
| import jwt from "jsonwebtoken"; | ||
|
|
||
| const METABASE_SITE_URL = "https://langfuse.metabaseapp.com"; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| try { | ||
| const METABASE_SITE_URL = "https://langfuse.metabaseapp.com"; | ||
| const METABASE_SECRET_KEY = process.env.METABASE_SECRET_KEY; | ||
| const secretKeyRaw = process.env.METABASE_SECRET_KEY; | ||
|
|
||
| if (!METABASE_SECRET_KEY) { | ||
| console.error("Missing required environment variables: METABASE_SECRET_KEY"); | ||
| if (!secretKeyRaw) { | ||
| console.error("Missing required environment variable: METABASE_SECRET_KEY"); | ||
| return NextResponse.json( | ||
| { message: "Server configuration error" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
|
|
||
| const secretKey = secretKeyRaw.trim(); | ||
|
|
||
| const dashboardId = request.nextUrl.searchParams.get("dashboardId") | ||
| ? parseInt(request.nextUrl.searchParams.get("dashboardId")!, 10) | ||
|
Check warning on line 21 in app/api/metabase-embed/route.ts
|
||
|
Comment on lines
+10
to
21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The emptiness check runs on Extended reasoning...Bug: The guard Code path: After the guard passes, Why existing code doesn't prevent it: The outer Impact: If someone accidentally pastes only whitespace into the Vercel env var UI for Fix: Swap the order — call const secretKey = (process.env.METABASE_SECRET_KEY ?? '').trim();
if (\!secretKey) {
console.error('Missing required environment variable: METABASE_SECRET_KEY');
return NextResponse.json({ message: 'Server configuration error' }, { status: 500 });
}Step-by-step proof:
|
||
| : 25; | ||
| const theme = request.nextUrl.searchParams.get("theme") === "day" ? "day" : "night"; | ||
| const theme = | ||
|
Check notice on line 23 in app/api/metabase-embed/route.ts
|
||
|
Comment on lines
20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟣 Pre-existing issue: passing a non-numeric Extended reasoning...What the bug is At The problematic serialization path
JSON.stringify({ x: NaN }) // '{x:null}'
Why existing code does not prevent it The ternary Impact For the normal application flow the impact is zero: the single internal caller ( Concrete step-by-step proof
Relationship to this PR This logic was not changed by the PR (which only trims the secret key and updates cache headers). It is a pre-existing issue, but the PR touches the same file and function, making it a natural opportunity to add the guard. Fix const raw = request.nextUrl.searchParams.get('dashboardId');
const parsed = raw ? parseInt(raw, 10) : NaN;
const dashboardId = \!isNaN(parsed) ? parsed : 25;Or return a 400 if an invalid value is explicitly supplied. |
||
| request.nextUrl.searchParams.get("theme") === "day" ? "day" : "night"; | ||
|
|
||
| const payload = { | ||
| resource: { dashboard: dashboardId }, | ||
| params: {}, | ||
| exp: Math.round(Date.now() / 1000) + 10 * 60, | ||
| }; | ||
|
|
||
| const token = jwt.sign(payload, METABASE_SECRET_KEY); | ||
| const token = jwt.sign(payload, secretKey); | ||
| const iframeUrl = `${METABASE_SITE_URL}/embed/dashboard/${token}#theme=${theme}&bordered=false&titled=false`; | ||
|
|
||
| return NextResponse.json( | ||
|
|
@@ -35,7 +39,7 @@ | |
| }, | ||
| { | ||
| headers: { | ||
| "Cache-Control": "public, s-maxage=300, max-age=0", | ||
| "Cache-Control": "no-store, no-cache, must-revalidate", | ||
| }, | ||
| } | ||
| ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
METABASE_SECRET_KEYis set to a whitespace-only string (e.g." \n"), the!secretKeyRawguard passes because a non-empty string is truthy. After.trim(), the value becomes"".jsonwebtokenaccepts an empty string and produces a token, but Metabase will reject it with the same "corrupt or manipulated" error. A post-trim emptiness check would close this gap and surface a clearer error message in the logs.Prompt To Fix With AI