Skip to content

fix(rollbar): odsiej z frontendu obce skrypty i zgłoszenia bez stack trace'u#680

Open
mpasternak wants to merge 2 commits into
devfrom
fix/rollbar-browser-szum
Open

fix(rollbar): odsiej z frontendu obce skrypty i zgłoszenia bez stack trace'u#680
mpasternak wants to merge 2 commits into
devfrom
fix/rollbar-browser-szum

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Problem

Trzy aktywne itemy browser-js okazały się nie naszymi błędami:

Item Co to naprawdę jest
#1477 Invalid regular expression: invalid group specifier name Plik: cdn.userway.org/widgetapp/…/widget_app_base.js — bundle widgetu dostępności UserWay. Lookbehind w regexie, nieobsługiwany przez Safari < 16.4 (zgłoszenia z Safari 15.6.1).
#444 (unknown): {} original_arg_types: ["string","htmllinkelement","undefined"], a w telemetrii tuż przed — fetch do api.userway.org. To zdarzenie error na elemencie <link> (nieudane ładowanie arkusza), serializowane przez Rollbara do bezużytecznego {}.
#502 Unexpected token = Chrome 64 na Androidzie 8 (luty 2018, Samsung J6). Brak filename i lineno — stack to sam komunikat. Pola klas w ciele class weszły w Chrome 72.

Żadnego nie da się naprawić w kodzie BPP ani nawet zdiagnozować — w dwóch
przypadkach nie wiadomo nawet, którego pliku dotyczą.

Rozwiązanie

checkIgnore odsiewa dwie klasy zgłoszeń:

  1. wszystkie ramki z obcego origin — cudzy skrypt, cudzy błąd,
  2. żadnej użytecznej ścieżki w żadnej ramce — brak ramek, "(unknown)"
    albo filename będący komunikatem błędu. Bez pliku i linii nie ma czego
    szukać.

Wystarczy jedna ramka z naszego origin, żeby raport przeszedł — mieszany
stos (nasz kod wywołany z obcego skryptu) nadal jest raportowany.

Predykat siedzi w osobnym pliku statycznym, nie w szablonie, żeby dało się
go przetestować. Jest zwykłym skryptem, nie modułemrollbar.html ładuje
go wcześnie w <head>, a type="module" odroczyłby wykonanie i część błędów
z czasu ładowania strony uciekłaby przed inicjalizacją Rollbara.

Filtr jest fail-open: brak skryptu albo wyjątek w samym filtrze → raportuj.
Filtr, który przez własną awarię chowa prawdziwe błędy, jest gorszy niż brak
filtra. Testy to sprawdzają (payload null/undefined/bez bodyfalse).

Testy

tests/js/rollbar-filters.test.js — 11 testów vitest, napisane przed
implementacją (RED: Failed to load url … rollbar-filters.js). Pokrywają
wszystkie trzy produkcyjne przypadki plus trace_chain (wyjątki łańcuchowe)
i zachowanie fail-open.

  • npx vitest run — 57 passed (6 plików)
  • src/django_bpp/tests — 108 passed (w tym test_brak_zewnetrznych_assetow)

Czego to NIE robi

Nie dodaje komunikatu „nieobsługiwana przeglądarka". Zebrane dane tego nie
uzasadniają: Safari 15.6.1 z #1477 jest w pełni sprawne — to bundle UserWaya
jest zbyt nowy, a Firefox 153 z #444 jest całkiem aktualny. Jedyna realnie
przestarzała przeglądarka to Chrome 64 z #502 (jedna sesja). Baner „twoja
przeglądarka jest nieobsługiwana" na wszystkich instalacjach to decyzja
produktowa, nie sprzątanie monitoringu — jeśli ma powstać, to osobno
i świadomie.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH

mpasternak and others added 2 commits July 25, 2026 00:29
…trace'u

Trzy aktywne itemy browser-js okazały się nie-naszymi błędami:

- #1477 "Invalid regular expression: invalid group specifier name" — plik to
  cdn.userway.org/widgetapp/.../widget_app_base.js, czyli bundle widgetu
  dostępności UserWay. Lookbehind w regexie, nieobsługiwany przez Safari
  < 16.4 (zgłoszenia z Safari 15.6.1). Ich kod, ich wydanie.
- #444 "(unknown): {}" — original_arg_types to ["string","htmllinkelement",
  "undefined"], a w telemetrii tuż przed tym fetch do api.userway.org. To
  zdarzenie error na elemencie <link> (nieudane ładowanie arkusza), które
  Rollbar serializuje do bezużytecznego "{}".
- #502 "Unexpected token =" — Chrome 64 na Androidzie 8 (luty 2018). Brak
  filename i lineno; stack to sam komunikat. Pola klas w ciele `class`
  weszły w Chrome 72.

Żadnego z nich nie da się naprawić w kodzie BPP ani nawet zdiagnozować.
checkIgnore odsiewa więc: (a) błędy, których WSZYSTKIE ramki pochodzą z
obcego origin, (b) zgłoszenia bez użytecznej ścieżki w żadnej ramce.

Predykat siedzi w osobnym pliku statycznym, nie w szablonie, żeby dało się go
przetestować (11 testów vitest). Jest zwykłym skryptem, nie modułem —
rollbar.html ładuje go wcześnie w <head>, a type="module" odroczyłby
wykonanie i część błędów z czasu ładowania strony uciekłaby.

Filtr jest fail-open: brak skryptu albo wyjątek w samym filtrze → raportuj.
Filtr, który przez własną awarię chowa prawdziwe błędy, jest gorszy niż brak
filtra.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH
Self-review wykrył dwie realne dziury w regule "brak użytecznej ścieżki →
wycisz", zmierzone na prawdziwych payloadach rollbar.js 3.1.0:

1. Ręczny `Rollbar.error("...")` buduje `body.message` BEZ `trace` i bez
   ramek — czyli był wyciszany zawsze. Dziś w BPP nie ma takiego wywołania,
   więc to nie regresja, tylko mina pod pierwsze użycie. Przy okazji ginął
   komunikat samego Rollbara o przekroczeniu rate-limitu, czyli sygnał
   "gubię itemy". Teraz: brak trace/trace_chain → NIGDY nie wyciszamy.

2. Item #502 (SyntaxError "Unexpected token =", Chrome 64) zaklasyfikowałem
   jako nieszkodliwy szum. Błędnie. Przeglądarka ujawnia treść błędu
   parsowania wyłącznie dla skryptów same-origin — obce bez CORS dostają
   gołe "Script error.". Konkretny komunikat oznacza więc, że któryś z
   NASZYCH statyków nie parsuje się na tej przeglądarce, czyli realną
   regresję kompatybilności. Wyciszenie tego skasowałoby jedyny ślad.

Nowa reguła jest węższa: wyciszamy zgłoszenie bez lokalizacji tylko wtedy,
gdy nie ma też treści (klasa "(unknown)" i komunikat "{}" — dokładnie item
#444). Sam brak lokalizacji nie wystarcza; klasa i komunikat wystarczą, żeby
zacząć szukać. Odrzucone obietnice z reason innym niż Error (ramka
"(unknown)") też przestają być zjadane.

#1477 (UserWay) nadal wyciszony — łapie go reguła obcego hosta, niezależna
od klasy wyjątku.

Dwa testy z poprzedniego commita kodowały starą, za szeroką regułę i
zostały poprawione razem z nią.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NcAqeqyqBzNEkkVnhpHDaH
@mpasternak

Copy link
Copy Markdown
Member Author

Poprawki po self-review — filtr zjadał NASZE błędy

Review zmierzył prawdziwe payloady rollbar.js 3.1.0 (zamiast wierzyć moim
odtworzonym kształtom) i znalazł dwie realne dziury. Obie naprawione
w 055f98d02.

1. Ręczny Rollbar.error("...") był wyciszany zawsze

rollbar 3.1.0 dla ręcznego logu buduje body.message bez trace
i bez ramek. Moje zbierzRamki zwracało [] → reguła „brak ścieżki → wycisz"
→ zgłoszenie znikało. Dziś w BPP nie ma żadnego takiego wywołania, więc to nie
regresja — tylko mina pod pierwsze użycie. Przy okazji ginął komunikat samego
Rollbara o przekroczeniu rate-limitu, czyli sygnał „gubię itemy".

Teraz: brak trace/trace_chain → nigdy nie wyciszamy. To są zgłoszenia,
które ktoś wysłał świadomie.

2. Item #502 to najpewniej NASZ bug — zaklasyfikowałem go błędnie

To jest korekta mojej analizy z początku sesji. Pisałem, że
SyntaxError: Unexpected token = z Chrome 64 jest „niediagnozowalny, brak
filename". Kluczowego faktu nie wziąłem pod uwagę: przeglądarka ujawnia treść
błędu parsowania wyłącznie dla skryptów same-origin
— obce bez CORS dostają
gołe "Script error.". Konkretny komunikat oznacza więc, że któryś
z naszych statyków nie parsuje się na tej przeglądarce, czyli realną
regresję kompatybilności. Wyciszenie skasowałoby jedyny ślad, i to na stałe.

Trop na przyszłość (z review): esbuild ma --target=es2018 (Gruntfile.js),
więc bundle powinien być czysty — podejrzenie pada na pliki serwowane
surowo, poza esbuildem (src/*/static/**/*.js + vendorowane node_modules
przez YarnFinder). W autorskim JS-ie BPP nie ma ??/?./class fields, więc
kandydat siedzi najpewniej w vendorowanej bibliotece. To osobne zadanie
tu tylko przestaję to wyciszać.

Nowa, węższa reguła

Wyciszamy zgłoszenie bez lokalizacji tylko gdy nie ma też treści — klasa
"(unknown)" i komunikat "{}", czyli dokładnie item #444. Sam brak
lokalizacji nie wystarcza: klasa i komunikat wystarczą, żeby zacząć szukać.
Dzięki temu przestają być zjadane także odrzucone obietnice z reason innym
niż Error (ramka "(unknown)").

#1477 (UserWay) nadal wyciszony — łapie go reguła obcego hosta, niezależna od
klasy wyjątku.

Dwa testy z poprzedniego commita kodowały starą, za szeroką regułę i
zostały poprawione razem z nią. Doszły testy na: body.message, rate-limit
Rollbara, odrzuconą obietnicę, SyntaxError bez ścieżki (raportuj) vs SyntaxError
z obcego hosta (wycisz), brak origin, host podszywający się pod nasz
(https://bpp.example.pl.evil.com). 18 testów, npx vitest run → 64 passed.

Potwierdzone przez review, bez zmian

  • Inline script w szablonie Django jest rozpoznawany jako nasz (filename
    = URL strony, matchuje przez origin + "/").
  • STATIC_URL = "/static/" jest zahardkodowany, brak override'u przez env,
    plus istniejący test_brak_zewnetrznych_assetow — statyki są same-origin.
  • window.location.origin: Chrome 8+/Safari 5.1+/FF 21+/IE11, a przy jego
    braku guard if (!origin) return false raportuje wszystko.
  • collectstatic zbiera plik automatycznie (AppDirectoriesFinder), nie trzeba
    nic dodawać do esbuilda; CI odpala npx vitest run bez listy plików, więc
    nowy test wchodzi sam.

Świadomie nie zmienione

  • "Script error." nadal wyciszany — zero informacji do działania.
  • Osobny plik zamiast inline w rollbar.html: review słusznie zauważa, że
    to jedno dodatkowe blokujące żądanie w <head>, dokładnie w oknie, które
    chcemy chronić. Zostawiam plik, bo inline'owany kod nie da się przetestować
    vitestem, a to właśnie testy wyłapały oba powyższe błędy. Koszt: jeden
    cache'owany request.
  • Origin z document.currentScript.src zamiast location.origin
    uodporniłoby filtr na przyszłe przestawienie STATIC_URL na CDN (dziś
    wyciszyłoby to 100% naszych błędów JS). Sensowne, ale przy zahardkodowanym
    STATIC_URL i teście pilnującym braku zewnętrznych assetów to nadmiar.

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