50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
|
|
/**
|
||
|
|
* Decides which server-side errors are worth sending to Sentry.
|
||
|
|
*
|
||
|
|
* Automated scanners probe for CMS paths that have never existed here
|
||
|
|
* (`/blog/wp/v2/posts/999999`, `/wp-login.php`, a bare `POST /`). React Router
|
||
|
|
* throws for each one — a 404 when no route matches, a 405 when a route has no
|
||
|
|
* `action` — and every throw reaches `handleError` in `app/entry.server.tsx`.
|
||
|
|
* Reporting those burns the Sentry quota without ever describing a real bug.
|
||
|
|
*/
|
||
|
|
import { isRouteErrorResponse } from "react-router";
|
||
|
|
|
||
|
|
/** React Router stamps `internal: true` on the errors it generates itself. */
|
||
|
|
function isInternalRouterError(error: unknown): boolean {
|
||
|
|
return (error as { internal?: unknown }).internal === true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** True when the request was linked from a page on this same origin. */
|
||
|
|
function hasSameOriginReferer(request: Request): boolean {
|
||
|
|
const referer = request.headers.get("referer");
|
||
|
|
if (!referer) return false;
|
||
|
|
try {
|
||
|
|
return new URL(referer).origin === new URL(request.url).origin;
|
||
|
|
} catch {
|
||
|
|
// Scanners send garbage in this header; a referer we can't parse isn't ours.
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Whether `error` should be reported to Sentry.
|
||
|
|
*
|
||
|
|
* Drops only the 4xx responses React Router generated for a request that
|
||
|
|
* matched nothing — that is, unrecognised URLs and methods. Everything else is
|
||
|
|
* reported, including responses the app threw deliberately (`internal: false`),
|
||
|
|
* so a 403 from an ownership check still shows up.
|
||
|
|
*
|
||
|
|
* The exception is a request carrying a same-origin `Referer`: a 404 reached
|
||
|
|
* from one of our own pages is a broken internal link, not a scanner, and stays
|
||
|
|
* visible in Sentry.
|
||
|
|
*/
|
||
|
|
export function shouldReportServerError(
|
||
|
|
error: unknown,
|
||
|
|
request: Request,
|
||
|
|
): boolean {
|
||
|
|
if (!isRouteErrorResponse(error)) return true;
|
||
|
|
if (!isInternalRouterError(error)) return true;
|
||
|
|
if (error.status < 400 || error.status > 499) return true;
|
||
|
|
return hasSameOriginReferer(request);
|
||
|
|
}
|