зеркало из https://github.com/mozilla/fxa.git
Merge pull request #17550 from mozilla/fxa-9998-add-sentry-payments
feat(next): add sentry to payments-next
This commit is contained in:
Коммит
73bd174c9d
|
@ -177,3 +177,6 @@ tmp
|
|||
# shared-cms
|
||||
libs/shared/cms/src/__generated__/graphql.d.ts
|
||||
libs/shared/cms/src/__generated__/graphql.js
|
||||
|
||||
# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
|
|
|
@ -79,5 +79,18 @@ STATS_D_CONFIG__PREFIX=
|
|||
CSP__ACCOUNTS_STATIC_CDN=https://accounts-static.cdn.mozilla.net
|
||||
CSP__PAYPAL_API='https://www.sandbox.paypal.com'
|
||||
|
||||
# Sentry Config
|
||||
SENTRY__SERVER_NAME=fxa-payments-next-server
|
||||
SENTRY__AUTH_TOKEN=
|
||||
|
||||
# Other
|
||||
CONTENT_SERVER_URL=http://localhost:3030
|
||||
|
||||
# Nextjs Public Environment Variables
|
||||
|
||||
# Sentry Config
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
NEXT_PUBLIC_SENTRY_ENV=local
|
||||
NEXT_PUBLIC_SENTRY_CLIENT_NAME=fxa-payments-next-client
|
||||
NEXT_PUBLIC_SENTRY_SAMPLE_RATE=1
|
||||
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=1
|
||||
|
|
|
@ -75,5 +75,18 @@ STATS_D_CONFIG__PREFIX=
|
|||
CSP__ACCOUNTS_STATIC_CDN=https://accounts-static.cdn.mozilla.net
|
||||
CSP__PAYPAL_API='https://www.paypal.com'
|
||||
|
||||
# Sentry Config
|
||||
SENTRY__SERVER_NAME=fxa-payments-next-server
|
||||
SENTRY__AUTH_TOKEN=
|
||||
|
||||
# Other
|
||||
CONTENT_SERVER_URL=https://accounts.firefox.com
|
||||
|
||||
# Nextjs Public Environment Variables
|
||||
|
||||
# Sentry Config
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
NEXT_PUBLIC_SENTRY_ENV=prod
|
||||
NEXT_PUBLIC_SENTRY_CLIENT_NAME=fxa-payments-next-client
|
||||
NEXT_PUBLIC_SENTRY_SAMPLE_RATE=1
|
||||
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=1
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use client';
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import NextError from 'next/error';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function GlobalError({
|
||||
error,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
}) {
|
||||
useEffect(() => {
|
||||
Sentry.captureException(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
{/* `NextError` is the default Next.js error page component. Its type
|
||||
definition requires a `statusCode` prop. However, since the App Router
|
||||
does not expose status codes for errors, we simply pass 0 to render a
|
||||
generic error message. */}
|
||||
<NextError statusCode={0} />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
|
@ -1,7 +1,18 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import 'reflect-metadata';
|
||||
import 'server-only';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsString, ValidateNested, IsDefined, IsUrl } from 'class-validator';
|
||||
import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
IsDefined,
|
||||
IsUrl,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
} from 'class-validator';
|
||||
import {
|
||||
RootConfig as NestAppRootConfig,
|
||||
validate,
|
||||
|
@ -20,6 +31,14 @@ class PaypalConfig {
|
|||
clientId!: string;
|
||||
}
|
||||
|
||||
class SentryServerConfig {
|
||||
@IsString()
|
||||
serverName!: string;
|
||||
|
||||
@IsString()
|
||||
authToken!: string;
|
||||
}
|
||||
|
||||
class AuthJSConfig {
|
||||
@IsUrl({ require_tld: false })
|
||||
issuerUrl!: string;
|
||||
|
@ -47,6 +66,11 @@ export class PaymentsNextConfig extends NestAppRootConfig {
|
|||
@IsDefined()
|
||||
csp!: CspConfig;
|
||||
|
||||
@Type(() => SentryServerConfig)
|
||||
@ValidateNested()
|
||||
@IsDefined()
|
||||
sentry!: SentryServerConfig;
|
||||
|
||||
@IsString()
|
||||
authSecret!: string;
|
||||
|
||||
|
@ -55,6 +79,29 @@ export class PaymentsNextConfig extends NestAppRootConfig {
|
|||
|
||||
@IsUrl({ require_tld: false })
|
||||
contentServerUrl!: string;
|
||||
|
||||
/**
|
||||
* Nextjs Public Environment Variables
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sentry Config
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
nextPublicSentryDsn?: string;
|
||||
|
||||
@IsString()
|
||||
nextPublicSentryEnv!: string;
|
||||
|
||||
@IsString()
|
||||
nextPublicSentryClientName!: string;
|
||||
|
||||
@IsNumber()
|
||||
nextPublicSentrySampleRate!: number;
|
||||
|
||||
@IsNumber()
|
||||
nextPublicSentryTracesSampleRate!: number;
|
||||
}
|
||||
|
||||
export const config = validate(process.env, PaymentsNextConfig);
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
await import('./sentry.server.config');
|
||||
const { getApp } = await import('@fxa/payments/ui/server');
|
||||
|
||||
await getApp().initialize();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { composePlugins, withNx } = require('@nx/next');
|
||||
const { withSentryConfig } = require('@sentry/nextjs');
|
||||
|
||||
/**
|
||||
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
|
||||
|
@ -22,6 +23,8 @@ const nextConfig = {
|
|||
'@nestjs/core',
|
||||
'@nestjs/common',
|
||||
'@nestjs/websockets',
|
||||
'@nestjs/graphql',
|
||||
'@nestjs/mapped-types',
|
||||
'class-transformer',
|
||||
'class-validator',
|
||||
'hot-shots',
|
||||
|
@ -50,9 +53,54 @@ const nextConfig = {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {import('@sentry/nextjs').SentryBuildOptions}
|
||||
**/
|
||||
const sentryOptions = {
|
||||
// For all available options, see:
|
||||
// https://github.com/getsentry/sentry-webpack-plugin#options
|
||||
|
||||
org: "mozilla",
|
||||
project: "fxa-payments-next",
|
||||
|
||||
// Enable source maps
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
|
||||
// Only print logs for uploading source maps in CI
|
||||
silent: !process.env.CI,
|
||||
|
||||
// For all available options, see:
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
||||
|
||||
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
||||
widenClientFileUpload: true,
|
||||
|
||||
// Automatically annotate React components to show their full name in breadcrumbs and session replay
|
||||
reactComponentAnnotation: {
|
||||
enabled: true,
|
||||
},
|
||||
|
||||
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
||||
// This can increase your server load as well as your hosting bill.
|
||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
||||
// side errors will fail.
|
||||
tunnelRoute: "/monitoring",
|
||||
|
||||
// Hides source maps from generated client bundles
|
||||
hideSourceMaps: true,
|
||||
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
disableLogger: true,
|
||||
}
|
||||
|
||||
// Use withSentryConfig to wrap the next config
|
||||
const sentryEnhancedConfig = (passedConfig) =>
|
||||
withSentryConfig(passedConfig, sentryOptions);
|
||||
|
||||
const plugins = [
|
||||
// Add more Next.js plugins to this list if needed.
|
||||
withNx,
|
||||
sentryEnhancedConfig,
|
||||
];
|
||||
|
||||
module.exports = composePlugins(...plugins)(nextConfig);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "payments-next",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"start": "next start"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file configures the initialization of Sentry on the client.
|
||||
// The config you add here will be used whenever a users loads a page in their browser.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
import { initSentryForNextjsClient } from '@fxa/shared/sentry/client';
|
||||
import { version } from './package.json';
|
||||
|
||||
const DEFAULT_SAMPLE_RATE = '1';
|
||||
const DEFAULT_TRACES_SAMPLE_RATE = '1';
|
||||
|
||||
const sentryConfig = {
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
env: process.env.NEXT_PUBLIC_SENTRY_ENV,
|
||||
clientName: process.env.NEXT_PUBLIC_SENTRY_CLIENT_NAME,
|
||||
sampleRate: parseInt(
|
||||
process.env.NEXT_PUBLIC_SENTRY_SAMPLE_RATE || DEFAULT_SAMPLE_RATE
|
||||
),
|
||||
tracesSampleRate: parseInt(
|
||||
process.env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE ||
|
||||
DEFAULT_TRACES_SAMPLE_RATE
|
||||
),
|
||||
};
|
||||
|
||||
initSentryForNextjsClient({
|
||||
release: version,
|
||||
sentry: sentryConfig,
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file configures the initialization of Sentry on the server.
|
||||
// The config you add here will be used whenever the server handles a request.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
import { initSentryForNextjsServer } from '@fxa/shared/sentry';
|
||||
import { config } from './config';
|
||||
import { version } from './package.json';
|
||||
|
||||
const sentryConfig = {
|
||||
dsn: config.nextPublicSentryDsn,
|
||||
env: config.nextPublicSentryEnv,
|
||||
serverName: config.sentry.serverName,
|
||||
sampleRate: config.nextPublicSentrySampleRate,
|
||||
tracesSampleRate: config.nextPublicSentryTracesSampleRate,
|
||||
};
|
||||
|
||||
initSentryForNextjsServer(
|
||||
{
|
||||
release: version,
|
||||
sentry: sentryConfig,
|
||||
},
|
||||
console
|
||||
);
|
|
@ -1,3 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import set from 'set-value';
|
||||
import { plainToInstance, ClassConstructor } from 'class-transformer';
|
||||
import { validateSync } from 'class-validator';
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
export * from './lib/next/client';
|
|
@ -8,3 +8,4 @@ export * from './lib/nest/sentry.constants';
|
|||
export * from './lib/reporting';
|
||||
export * from './lib/node';
|
||||
export * from './lib/browser';
|
||||
export * from './lib/next/server';
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file configures the initialization of Sentry on the client.
|
||||
// The config you add here will be used whenever a users loads a page in their browser.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import { SentryConfigOpts } from '../models/SentryConfigOpts';
|
||||
import { buildSentryConfig } from '../config-builder';
|
||||
import { Logger } from '../sentry.types';
|
||||
import { beforeSend } from '../utils/beforeSend.client';
|
||||
|
||||
/**
|
||||
* @@todo - To be worked on in FXA-10398
|
||||
*/
|
||||
const sentryEnabled = true;
|
||||
|
||||
export function initSentryForNextjsClient(
|
||||
config: SentryConfigOpts,
|
||||
log?: Logger
|
||||
) {
|
||||
if (!log) {
|
||||
log = console;
|
||||
}
|
||||
|
||||
if (!config?.sentry?.dsn) {
|
||||
log.error('No Sentry dsn provided');
|
||||
return;
|
||||
}
|
||||
|
||||
// We want sentry to be disabled by default... This is because we only emit data
|
||||
// for users that 'have opted in'. A subsequent call to 'enable' is needed to ensure
|
||||
// that sentry events only flow under the proper circumstances.
|
||||
//disable();
|
||||
|
||||
const opts = buildSentryConfig(config, log);
|
||||
try {
|
||||
Sentry.init({
|
||||
...opts,
|
||||
integrations: [
|
||||
Sentry.browserTracingIntegration({
|
||||
enableInp: true,
|
||||
}),
|
||||
],
|
||||
beforeSend: function (event: Sentry.ErrorEvent) {
|
||||
return beforeSend(sentryEnabled, opts, event);
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import { ErrorEvent } from '@sentry/types';
|
||||
import { SentryConfigOpts } from '../models/SentryConfigOpts';
|
||||
import { buildSentryConfig } from '../config-builder';
|
||||
import { Logger } from '../sentry.types';
|
||||
import { tagFxaName } from '../utils/tagFxaName';
|
||||
|
||||
type ExtraOpts = {
|
||||
integrations?: any[];
|
||||
eventFilters?: Array<(event: ErrorEvent, hint: any) => ErrorEvent>;
|
||||
};
|
||||
|
||||
type InitSentryOpts = SentryConfigOpts & ExtraOpts;
|
||||
|
||||
export function initSentryForNextjsServer(config: InitSentryOpts, log: Logger) {
|
||||
if (!config?.sentry?.dsn) {
|
||||
log.error('No Sentry dsn provided. Cannot start sentry');
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = buildSentryConfig(config, log);
|
||||
/**
|
||||
* @@todo - Move to lib/utils/beforeSend.server.ts - FXA-10402
|
||||
*/
|
||||
const beforeSend = function (event: ErrorEvent, hint: any) {
|
||||
// Default
|
||||
event = tagFxaName(event, config.sentry?.serverName || 'unknown');
|
||||
|
||||
// Custom filters
|
||||
config.eventFilters?.forEach((filter) => {
|
||||
event = filter(event, hint);
|
||||
});
|
||||
return event;
|
||||
};
|
||||
|
||||
const integrations = [
|
||||
// Default
|
||||
Sentry.extraErrorDataIntegration({ depth: 5 }),
|
||||
|
||||
// Custom Integrations
|
||||
...(config.integrations || []),
|
||||
];
|
||||
|
||||
try {
|
||||
Sentry.init({
|
||||
// Defaults Options
|
||||
normalizeDepth: 6,
|
||||
maxValueLength: 500,
|
||||
|
||||
// Custom Options
|
||||
integrations,
|
||||
beforeSend,
|
||||
...opts,
|
||||
});
|
||||
} catch (e) {
|
||||
log.debug('init-sentry', { msg: 'Issue initializing sentry!' });
|
||||
log.error('init-sentry', e);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,9 @@ export function initSentry(config: InitSentryOpts, log: Logger) {
|
|||
}
|
||||
|
||||
const opts = buildSentryConfig(config, log);
|
||||
/**
|
||||
* @@todo - Move to lib/utils/beforeSend.server.ts - FXA-10402
|
||||
*/
|
||||
const beforeSend = function (event: ErrorEvent, hint: any) {
|
||||
// Default
|
||||
event = tagFxaName(event, config.sentry?.serverName || 'unknown');
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { SentryConfigOpts } from '../models/SentryConfigOpts';
|
||||
import { beforeSend } from './beforeSend.client';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
const config: SentryConfigOpts = {
|
||||
release: 'v0.0.0',
|
||||
sentry: {
|
||||
dsn: 'https://public:private@host:8080/1',
|
||||
env: 'test',
|
||||
clientName: 'fxa-shared-testing',
|
||||
sampleRate: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const sentryEnabled = true;
|
||||
|
||||
describe('beforeSend', () => {
|
||||
it('works without request url', () => {
|
||||
const data = {
|
||||
key: 'value',
|
||||
} as unknown as Sentry.ErrorEvent;
|
||||
|
||||
const resultData = beforeSend(sentryEnabled, config, data);
|
||||
|
||||
expect(data).toEqual(resultData);
|
||||
});
|
||||
|
||||
it('fingerprints errno', () => {
|
||||
const data = {
|
||||
request: {
|
||||
url: 'https://example.com',
|
||||
},
|
||||
tags: {
|
||||
errno: '100',
|
||||
},
|
||||
type: undefined,
|
||||
} as Sentry.ErrorEvent;
|
||||
|
||||
const resultData = beforeSend(sentryEnabled, config, data);
|
||||
expect(resultData?.fingerprint?.[0]).toEqual('errno100');
|
||||
expect(resultData?.level).toEqual('info');
|
||||
});
|
||||
|
||||
it('properly erases sensitive information from url', () => {
|
||||
const url = 'https://accounts.firefox.com/complete_reset_password';
|
||||
const badQuery =
|
||||
'?token=foo&code=bar&email=some%40restmail.net&service=sync';
|
||||
const goodQuery = '?token=VALUE&code=VALUE&email=VALUE&service=sync';
|
||||
const badData = {
|
||||
request: {
|
||||
url: url + badQuery,
|
||||
},
|
||||
} as Sentry.ErrorEvent;
|
||||
|
||||
const goodData = {
|
||||
request: {
|
||||
url: url + goodQuery,
|
||||
},
|
||||
};
|
||||
|
||||
const resultData = beforeSend(sentryEnabled, config, badData);
|
||||
expect(resultData?.request?.url).toEqual(goodData.request.url);
|
||||
});
|
||||
|
||||
it('properly erases sensitive information from referrer', () => {
|
||||
const url = 'https://accounts.firefox.com/complete_reset_password';
|
||||
const badQuery =
|
||||
'?token=foo&code=bar&email=some%40restmail.net&service=sync';
|
||||
const goodQuery = '?token=VALUE&code=VALUE&email=VALUE&service=sync';
|
||||
const badData = {
|
||||
request: {
|
||||
headers: {
|
||||
Referer: url + badQuery,
|
||||
},
|
||||
},
|
||||
type: undefined,
|
||||
} as Sentry.ErrorEvent;
|
||||
|
||||
const goodData = {
|
||||
request: {
|
||||
headers: {
|
||||
Referer: url + goodQuery,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultData = beforeSend(sentryEnabled, config, badData);
|
||||
expect(resultData?.request?.headers?.Referer).toEqual(
|
||||
goodData.request.headers.Referer
|
||||
);
|
||||
});
|
||||
|
||||
it('properly erases sensitive information from abs_path', () => {
|
||||
const url = 'https://accounts.firefox.com/complete_reset_password';
|
||||
const badCulprit = 'https://accounts.firefox.com/scripts/57f6d4e4.main.js';
|
||||
const badAbsPath =
|
||||
'https://accounts.firefox.com/complete_reset_password?token=foo&code=bar&email=a@a.com&service=sync&resume=barbar';
|
||||
const goodAbsPath =
|
||||
'https://accounts.firefox.com/complete_reset_password?token=VALUE&code=VALUE&email=VALUE&service=sync&resume=VALUE';
|
||||
const data = {
|
||||
culprit: badCulprit,
|
||||
exception: {
|
||||
values: [
|
||||
{
|
||||
stacktrace: {
|
||||
frames: [
|
||||
{
|
||||
abs_path: badAbsPath, // eslint-disable-line camelcase
|
||||
},
|
||||
{
|
||||
abs_path: badAbsPath, // eslint-disable-line camelcase
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
request: {
|
||||
url,
|
||||
},
|
||||
type: undefined,
|
||||
};
|
||||
|
||||
const resultData = beforeSend(sentryEnabled, config, data);
|
||||
|
||||
expect(
|
||||
resultData?.exception?.values?.[0].stacktrace?.frames?.[0].abs_path
|
||||
).toEqual(goodAbsPath);
|
||||
expect(
|
||||
resultData?.exception?.values?.[0].stacktrace?.frames?.[1].abs_path
|
||||
).toEqual(goodAbsPath);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Change to @sentry/browser after upgrade to Sentry 8
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import { cleanUpQueryParam } from './cleanUpQueryParam';
|
||||
import { SentryConfigOpts } from '../models/SentryConfigOpts';
|
||||
import { tagFxaName } from './tagFxaName';
|
||||
|
||||
/**
|
||||
* function that gets called before data gets sent to error metrics
|
||||
*
|
||||
* @param {Object} event
|
||||
* Error object data
|
||||
* @returns {Object} data
|
||||
* Modified error object data
|
||||
* @private
|
||||
*/
|
||||
export function beforeSend(
|
||||
sentryEnabled: boolean,
|
||||
opts: SentryConfigOpts,
|
||||
event: Sentry.ErrorEvent
|
||||
) {
|
||||
if (sentryEnabled === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (event.request) {
|
||||
if (event.request.url) {
|
||||
event.request.url = cleanUpQueryParam(event.request.url);
|
||||
}
|
||||
|
||||
if (event.tags) {
|
||||
// if this is a known errno, then use grouping with fingerprints
|
||||
// Docs: https://docs.sentry.io/hosted/learn/rollups/#fallback-grouping
|
||||
if (event.tags.errno) {
|
||||
event.fingerprint = ['errno' + (event.tags.errno as number)];
|
||||
// if it is a known error change the error level to info.
|
||||
event.level = 'info';
|
||||
}
|
||||
}
|
||||
|
||||
if (event.exception?.values) {
|
||||
event.exception.values.forEach((value: Sentry.Exception) => {
|
||||
if (value.stacktrace && value.stacktrace.frames) {
|
||||
value.stacktrace.frames.forEach((frame: { abs_path?: string }) => {
|
||||
if (frame.abs_path) {
|
||||
frame.abs_path = cleanUpQueryParam(frame.abs_path); // eslint-disable-line camelcase
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (event.request.headers?.Referer) {
|
||||
event.request.headers.Referer = cleanUpQueryParam(
|
||||
event.request.headers.Referer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
event = tagFxaName(event, opts.sentry?.clientName || opts.sentry?.serverName);
|
||||
return event;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { cleanUpQueryParam } from './cleanUpQueryParam';
|
||||
|
||||
describe('cleanUpQueryParam', () => {
|
||||
it('properly erases sensitive information', () => {
|
||||
const fixtureUrl1 =
|
||||
'https://accounts.firefox.com/complete_reset_password?token=foo&code=bar&email=some%40restmail.net';
|
||||
const expectedUrl1 =
|
||||
'https://accounts.firefox.com/complete_reset_password?token=VALUE&code=VALUE&email=VALUE';
|
||||
const resultUrl1 = cleanUpQueryParam(fixtureUrl1);
|
||||
|
||||
expect(resultUrl1).toEqual(expectedUrl1);
|
||||
});
|
||||
|
||||
it('properly erases sensitive information, keeps allowed fields', () => {
|
||||
const fixtureUrl2 =
|
||||
'https://accounts.firefox.com/signup?client_id=foo&service=sync';
|
||||
const expectedUrl2 =
|
||||
'https://accounts.firefox.com/signup?client_id=foo&service=sync';
|
||||
const resultUrl2 = cleanUpQueryParam(fixtureUrl2);
|
||||
|
||||
expect(resultUrl2).toEqual(expectedUrl2);
|
||||
});
|
||||
|
||||
it('properly returns the url when there is no query', () => {
|
||||
const expectedUrl = 'https://accounts.firefox.com/signup';
|
||||
const resultUrl = cleanUpQueryParam(expectedUrl);
|
||||
|
||||
expect(resultUrl).toEqual(expectedUrl);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Query parameters we allow to propagate to sentry
|
||||
*/
|
||||
const ALLOWED_QUERY_PARAMETERS = [
|
||||
'automatedBrowser',
|
||||
'client_id',
|
||||
'context',
|
||||
'entrypoint',
|
||||
'keys',
|
||||
'migration',
|
||||
'redirect_uri',
|
||||
'scope',
|
||||
'service',
|
||||
'setting',
|
||||
'style',
|
||||
];
|
||||
|
||||
/**
|
||||
* Overwrites sensitive query parameters with a dummy value.
|
||||
*
|
||||
* @param url
|
||||
* @returns url
|
||||
*/
|
||||
export function cleanUpQueryParam(url = '') {
|
||||
const urlObj = new URL(url);
|
||||
|
||||
if (!urlObj.search.length) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Iterate the search parameters.
|
||||
urlObj.searchParams.forEach((_, key) => {
|
||||
if (!ALLOWED_QUERY_PARAMETERS.includes(key)) {
|
||||
// if the param is a PII (not allowed) then reset the value.
|
||||
urlObj.searchParams.set(key, 'VALUE');
|
||||
}
|
||||
});
|
||||
|
||||
return urlObj.href;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/** Adds fxa.name to data.tags */
|
||||
export function tagFxaName(data: any, name?: string) {
|
||||
data.tags = data.tags || {};
|
||||
data.tags['fxa.name'] = name || 'unknown';
|
||||
return data;
|
||||
}
|
|
@ -78,6 +78,7 @@
|
|||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@sentry/browser": "^7.113.0",
|
||||
"@sentry/integrations": "^7.113.0",
|
||||
"@sentry/nextjs": "^8",
|
||||
"@sentry/node": "^7.113.0",
|
||||
"@sentry/opentelemetry-node": "^7.113.0",
|
||||
"@swc/helpers": "0.5.11",
|
||||
|
@ -278,7 +279,8 @@
|
|||
"tap/typescript": "^4.5.2",
|
||||
"terser:>4.0.0 <5": ">=4.8.1",
|
||||
"terser:>5 <6": ">=5.14.2",
|
||||
"underscore": ">=1.13.2"
|
||||
"underscore": ">=1.13.2",
|
||||
"@sentry/types": "^7.113.0"
|
||||
},
|
||||
"packageManager": "yarn@3.3.0",
|
||||
"_moduleAliases": {
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
"@fxa/shared/pem-jwk": ["libs/shared/pem-jwk/src/index.ts"],
|
||||
"@fxa/shared/react": ["libs/shared/react/src/index.ts"],
|
||||
"@fxa/shared/sentry": ["libs/shared/sentry/src/index.ts"],
|
||||
"@fxa/shared/sentry/client": ["libs/shared/sentry/src/client.ts"],
|
||||
"@fxa/vendored/common-password-list": [
|
||||
"libs/vendored/common-password-list/src/index.ts"
|
||||
],
|
||||
|
|
838
yarn.lock
838
yarn.lock
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче