Skip to content

Drop crashes thrown by code injected inline into our pages - #310

Merged
SpikeyCoder merged 5 commits into
mainfrom
fix/injected-inline-script-crash
Oct 29, 2026
Merged

SpikeyCoder merged 5 commits into
mainfrom
fix/injected-inline-script-crash

Conversation

@SpikeyCoder

@SpikeyCoder SpikeyCoder commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Fixes Trello card [CRASH] TypeError: undefined is not an object (evaluating ) (https://trello.com/c/y2bVJwQO), seen on /results.

Root cause

The report's only stack frame is global code@https://fundermatch.org/results:1:16, which is inline code in the page document itself. Our CSP (script-src 'self' https://gc.zgo.at, set in index.html, public/_headers and public/_worker.js) allows no inline scripts, so the code that threw was injected by the browser or an extension (a user script, which runs despite the CSP). It's from the same family as the chrome-extension:// frames we already filter, and there's nothing in our code to fix.

Change

  • isNoise (in src/lib/monitoring.ts) now drops a crash whose top frame (the first one with a URL) is a page on the live site rather than a .js/.mjs/.cjs script. The new isOurPage helper parses the frame URL and reuses PRODUCTION_HOST.
  • Crashes that brought up the error screen are still reported, as with the network-error rule.
  • Still reported: our bundle's frames above an inline frame, same-origin script files, other sites' URLs and look-alike hosts.
  • Comments next to all three CSP definitions point at this rule, since it relies on there being no inline scripts.

Tests (written first, red before the fix)

New cases in tests/monitoring.test.mjs:

  • dropped: the exact frame from the card, Chrome's frame format, /, www, :443, the error event without an error object, an unhandled rejection
  • still reported: bundle frames, a same-origin non-bundle script, a third-party URL, a look-alike host, error-screen crashes
  • a chunk-load error pinned on the document still follows the chunk-reload rules

npm test (75/75 after npm run build), npm run lint and tsc -b all pass.

Reviews

Four /code-review rounds, with the fixes in follow-up commits. Two security reviews found nothing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h

The /results crash on the bug board ("TypeError: undefined is not an
object", Brave on macOS) has a single frame,
"global code@https://fundermatch.org/results:1:16": inline code in the
page document itself. Our CSP allows no inline scripts and all our code is
served from /assets/*.js, so that code was injected by the browser or an
extension (a user script), which content scripts can do despite the CSP.

isNoise now treats a crash whose top frame is one of our own pages (not a
script file) like one whose top frame is an extension's. Frames from our
bundle above it still count as ours, and other sites' URLs are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Deploying funder-finder with  Cloudflare Pages  Cloudflare Pages

Latest commit: 329ae9c
Status: ✅  Deploy successful!
Preview URL: https://c7fad6b8.funder-finder.pages.dev
Branch Preview URL: https://fix-injected-inline-script-c.funder-finder.pages.dev

View logs

Review feedback: an explicit :443 left the path unparsed, so a frame from
our own bundle counted as injected; and the rule now says what it means
(our code is served from /assets/) instead of keying on a .js extension.
PRODUCTION_HOST goes back where it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h
Review round 2: a crash that brought up the error screen is reported even
when its top frame is a page, like the network rule. The page check parses
the frame's URL and reuses PRODUCTION_HOST, so any port or scheme works and
the host lives in one place. Tests pin the error-screen case and Safari's
failed module import pinned on the document (still a chunk-load error).
The CSP comment in index.html points at the rule that depends on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h
Review round 3: a same-origin script file outside /assets/ is not a page,
so it no longer counts as injected; the check sits with its own comment
instead of splitting the network rule's; a test pins Vite's base and asset
dir, which the check relies on; and the CSP notes sit beside all three
policies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h
Review round 4: every file of our bundle ends in .js, so the extension
test alone tells a script from a page, and the test pinning vite.config's
text (which checked the source, not the build) goes with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GNg1jsnDVcemV3Ut9XhA7h
@SpikeyCoder
SpikeyCoder merged commit f6d9854 into main Oct 29, 2026
3 checks passed
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.

2 participants