brackt/app/lib/error-reporting.ts

50 lines
1.9 KiB
TypeScript
Raw Normal View History

/**
* 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);
}