chore(sentry): filter PlayerControlsInterface + extension-wrapped fetch noise (#3400)

* chore(sentry): filter PlayerControlsInterface + extension-wrapped fetch noise

Triaged unresolved Sentry issues — added two targeted noise filters:

- WORLDMONITOR-P2: ignoreErrors entry for /PlayerControlsInterface\.\w+ is
  not a function/ (Android Chrome WebView native bridge injection, like
  the existing _pcmBridgeCallbackHandler / hybridExecute patterns).
- WORLDMONITOR-P5: beforeSend guard suppressing TypeError 'Failed to fetch
  (<host>)' when any frame is a browser-extension URL. Some AdBlock-class
  extensions wrap window.fetch and their replacement can fail for reasons
  unrelated to our backend; the existing maplibre host-allowlist doesn't
  cover our own hosts (abacus.worldmonitor.app, api.worldmonitor.app), and
  gating on the extension frame keeps signal for genuine first-party
  fetch failures from users without such extensions.

P1 (Dodo declined) and P4 (FRED 520) are intentional ops captures — left
unfiltered and resolved in next release. P3 (dyn-import) follows the
established policy that first-party stack frames must surface and is
mitigated by installChunkReloadGuard; resolved without a filter.

* fix(sentry): gate P5 extension-fetch filter on !hasFirstParty + add regression tests

Per PR review: the new `Failed to fetch (<host>)` extension-frame filter
needs the same `!hasFirstParty` gate the broader extension rule already
uses. Without it, a real first-party regression like a panels-*.js fetch
to api.worldmonitor.app would be silenced for users who happen to run
fetch-wrapping extensions.

Added two regression tests that lock in the safety property: extension-
only stacks suppress (the original P5 case); first-party + extension
mixed stacks reach Sentry (api.worldmonitor.app outage scenario).

* refactor(sentry): drop redundant P5 filter, retain regression tests for existing extension rule
This commit is contained in:
Elie Habib
2026-04-25 16:43:17 +04:00
committed by GitHub
parent 2f5445284b
commit 7e68b30eb8
2 changed files with 24 additions and 0 deletions

View File

@@ -363,6 +363,29 @@ describe('existing beforeSend filters', () => {
assert.ok(beforeSend(event) !== null, 'All-maplibre first-party tile fetch failure must still reach Sentry');
});
it('suppresses "Failed to fetch (<host>)" when stack is extension-only (covered by generic extension rule)', () => {
// WORLDMONITOR-P5: AdBlock-class extensions wrap window.fetch and their
// replacement can fail unrelated to our backend. The generic extension rule
// (`!hasFirstParty && extension frame`) already drops this; the test locks
// that property in for the `Failed to fetch (<host>)` message shape.
const event = makeEvent('Failed to fetch (abacus.worldmonitor.app)', 'TypeError', [
{ filename: 'chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js', lineno: 2, function: 'window.fetch' },
]);
assert.equal(beforeSend(event), null, 'Extension-only fetch failure should be suppressed');
});
it('does NOT suppress "Failed to fetch (<host>)" when stack has both first-party and extension frames', () => {
// Safety property: a first-party panels-*.js frame means our code initiated
// the fetch — must surface even if an extension also wrapped it, so a real
// api.worldmonitor.app outage isn't silenced for users who happen to run
// fetch-wrapping extensions.
const event = makeEvent('Failed to fetch (api.worldmonitor.app)', 'TypeError', [
{ filename: '/assets/panels-wF5GXf0N.js', lineno: 24, function: 'window.fetch' },
{ filename: 'chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js', lineno: 2, function: 'window.fetch' },
]);
assert.ok(beforeSend(event) !== null, 'First-party + extension Failed-to-fetch must reach Sentry');
});
it('suppresses iOS Safari WKWebView "Cannot inject key into script value" regardless of first-party frame', () => {
// The native throw always lands in a first-party caller; the existing
// !hasFirstParty gate missed it. `UnknownError` type name is WebKit-only