From 14b93416473e21bf1d1ddfd0e97e688b58c1e5ec Mon Sep 17 00:00:00 2001 From: Dan Schomburg <94418270+dschom@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:57:50 -0700 Subject: [PATCH] task(admin-server): Fix startup times and some refactor Because: - We want to improve startup performance of admin-server - We determined using the @sentry/node package in the admin-server was source of slowdown - Using @sentry/nestjs made a big improvement in startup time. This Commit: - Switches to using @sentry/nestjs integration for admin-server - Refactors libs/sentry into three distinct contexts libs/sentry-nestjs, libs/sentry-node, libs/sentry-browser, and libs/sentry-utils - Extracts common functions to libs/sentry-utils so they can be reused. --- libs/shared/l10n/src/lib/l10n.utils.ts | 4 +- libs/shared/log/src/index.ts | 2 +- libs/shared/log/src/lib/logging.ts | 7 + libs/shared/otel/.eslintrc.json | 18 + libs/shared/otel/README.md | 11 + libs/shared/otel/jest.config.ts | 11 + libs/shared/otel/package.json | 4 + libs/shared/otel/project.json | 29 + libs/shared/otel/src/index.ts | 7 + libs/shared/otel/src/lib/config.ts | 135 ++ .../otel/src/lib/exporters/exporters.ts | 25 + .../otel/src/lib/exporters/fxa-console.ts | 48 + libs/shared/otel/src/lib/exporters/fxa-gcp.ts | 56 + .../shared/otel/src/lib/exporters/fxa-otlp.ts | 74 + libs/shared/otel/src/lib/exporters/util.ts | 45 + libs/shared/otel/src/lib/node-tracing.spec.ts | 26 + libs/shared/otel/src/lib/node-tracing.ts | 167 ++ libs/shared/otel/src/lib/pii-filters.ts | 80 + .../otel/src/lib/providers/node-provider.ts | 23 + .../otel/src/lib/providers/web-provider.ts | 23 + libs/shared/otel/tsconfig.json | 22 + libs/shared/otel/tsconfig.lib.json | 10 + libs/shared/otel/tsconfig.spec.json | 14 + libs/shared/sentry-browser/.eslintrc.json | 18 + libs/shared/sentry-browser/README.md | 11 + libs/shared/sentry-browser/jest.config.ts | 11 + libs/shared/sentry-browser/package.json | 8 + libs/shared/sentry-browser/project.json | 28 + libs/shared/sentry-browser/src/index.ts | 1 + .../sentry-browser/src/lib/browser.spec.ts | 45 + libs/shared/sentry-browser/src/lib/browser.ts | 68 + libs/shared/sentry-browser/tsconfig.json | 22 + libs/shared/sentry-browser/tsconfig.lib.json | 10 + libs/shared/sentry-browser/tsconfig.spec.json | 14 + libs/shared/sentry-nest/.eslintrc.json | 18 + libs/shared/sentry-nest/README.md | 11 + libs/shared/sentry-nest/jest.config.ts | 11 + libs/shared/sentry-nest/package.json | 8 + libs/shared/sentry-nest/project.json | 28 + libs/shared/sentry-nest/src/index.ts | 2 + libs/shared/sentry-nest/src/lib/nest.ts | 50 + .../sentry-nest/src/lib/reporting.spec.ts | 99 ++ libs/shared/sentry-nest/src/lib/reporting.ts | 157 ++ libs/shared/sentry-nest/tsconfig.json | 22 + libs/shared/sentry-nest/tsconfig.lib.json | 10 + libs/shared/sentry-nest/tsconfig.spec.json | 14 + libs/shared/sentry-next/.eslintrc.json | 18 + libs/shared/sentry-next/README.md | 11 + libs/shared/sentry-next/jest.config.ts | 11 + libs/shared/sentry-next/package.json | 8 + libs/shared/sentry-next/project.json | 28 + libs/shared/sentry-next/src/index.ts | 2 + libs/shared/sentry-next/src/lib/client.ts | 46 + libs/shared/sentry-next/src/lib/server.ts | 53 + libs/shared/sentry-next/tsconfig.json | 22 + libs/shared/sentry-next/tsconfig.lib.json | 10 + libs/shared/sentry-next/tsconfig.spec.json | 14 + libs/shared/sentry-node/.eslintrc.json | 18 + libs/shared/sentry-node/README.md | 11 + libs/shared/sentry-node/jest.config.ts | 11 + libs/shared/sentry-node/package.json | 8 + libs/shared/sentry-node/project.json | 28 + libs/shared/sentry-node/src/index.ts | 3 + .../src/lib/joi-message-overrides.spec.ts | 25 + .../src/lib/joi-message-overrides.ts | 40 + libs/shared/sentry-node/src/lib/node.ts | 50 + .../src/lib/report-validation-error.ts | 60 + libs/shared/sentry-node/tsconfig.json | 22 + libs/shared/sentry-node/tsconfig.lib.json | 10 + libs/shared/sentry-node/tsconfig.spec.json | 14 + libs/shared/sentry-utils/.eslintrc.json | 18 + libs/shared/sentry-utils/README.md | 11 + libs/shared/sentry-utils/jest.config.ts | 11 + libs/shared/sentry-utils/package.json | 8 + libs/shared/sentry-utils/project.json | 27 + libs/shared/sentry-utils/src/index.ts | 9 + .../src/lib/before-send.browser.spec.ts | 167 ++ .../src/lib/before-send.browser.ts | 74 + .../src/lib/before-send.server.spec.ts | 33 + .../src/lib/before-send.server.ts | 29 + .../src/lib/config-builder.spec.ts | 133 ++ .../sentry-utils/src/lib/config-builder.ts | 87 + .../shared/sentry-utils/src/lib/models/pii.ts | 41 + .../src/lib/models/sentry-config-opts.ts | 41 + .../src/lib/pii/filter-actions.spec.ts | 380 ++++ .../src/lib/pii/filter-actions.ts | 338 ++++ .../sentry-utils/src/lib/pii/filters.spec.ts | 355 ++++ .../sentry-utils/src/lib/pii/filters.ts | 193 ++ .../sentry-utils/src/lib/sentry.types.ts | 10 + .../shared/sentry-utils/src/lib/utils.spec.ts | 99 ++ libs/shared/sentry-utils/src/lib/utils.ts | 93 + libs/shared/sentry-utils/tsconfig.json | 22 + libs/shared/sentry-utils/tsconfig.lib.json | 10 + libs/shared/sentry-utils/tsconfig.spec.json | 14 + .../sentry/src/lib/pii/filter-actions.ts | 10 +- package.json | 20 +- packages/fxa-admin-server/package.json | 2 +- packages/fxa-admin-server/src/app.module.ts | 15 +- packages/fxa-admin-server/src/config/index.ts | 2 +- .../src/gql/account/account.resolver.ts | 2 + .../fxa-admin-server/src/gql/gql.module.ts | 6 + packages/fxa-admin-server/src/main.ts | 15 +- packages/fxa-admin-server/src/monitoring.ts | 26 +- .../src/scripts/audit-tokens.spec.ts | 2 - .../src/subscriptions/appstore.service.ts | 2 +- .../src/subscriptions/stripe.service.spec.ts | 5 +- .../src/subscriptions/stripe.service.ts | 3 +- packages/fxa-admin-server/tsconfig.build.json | 5 +- packages/fxa-shared/test/tracing/exporters.ts | 13 +- .../fxa-shared/tracing/exporters/exporters.ts | 8 +- .../tracing/exporters/fxa-console.ts | 2 +- .../fxa-shared/tracing/exporters/fxa-gcp.ts | 5 +- .../fxa-shared/tracing/exporters/fxa-otlp.ts | 2 +- .../tracing/exporters/fxa-sentry.ts | 42 - packages/fxa-shared/tracing/exporters/util.ts | 2 +- packages/fxa-shared/tracing/node-tracing.ts | 2 - .../tracing/providers/node-provider.ts | 2 +- tsconfig.base.json | 10 +- yarn.lock | 1553 +++++++---------- 119 files changed, 4951 insertions(+), 1038 deletions(-) create mode 100644 libs/shared/otel/.eslintrc.json create mode 100644 libs/shared/otel/README.md create mode 100644 libs/shared/otel/jest.config.ts create mode 100644 libs/shared/otel/package.json create mode 100644 libs/shared/otel/project.json create mode 100644 libs/shared/otel/src/index.ts create mode 100644 libs/shared/otel/src/lib/config.ts create mode 100644 libs/shared/otel/src/lib/exporters/exporters.ts create mode 100644 libs/shared/otel/src/lib/exporters/fxa-console.ts create mode 100644 libs/shared/otel/src/lib/exporters/fxa-gcp.ts create mode 100644 libs/shared/otel/src/lib/exporters/fxa-otlp.ts create mode 100644 libs/shared/otel/src/lib/exporters/util.ts create mode 100644 libs/shared/otel/src/lib/node-tracing.spec.ts create mode 100644 libs/shared/otel/src/lib/node-tracing.ts create mode 100644 libs/shared/otel/src/lib/pii-filters.ts create mode 100644 libs/shared/otel/src/lib/providers/node-provider.ts create mode 100644 libs/shared/otel/src/lib/providers/web-provider.ts create mode 100644 libs/shared/otel/tsconfig.json create mode 100644 libs/shared/otel/tsconfig.lib.json create mode 100644 libs/shared/otel/tsconfig.spec.json create mode 100644 libs/shared/sentry-browser/.eslintrc.json create mode 100644 libs/shared/sentry-browser/README.md create mode 100644 libs/shared/sentry-browser/jest.config.ts create mode 100644 libs/shared/sentry-browser/package.json create mode 100644 libs/shared/sentry-browser/project.json create mode 100644 libs/shared/sentry-browser/src/index.ts create mode 100644 libs/shared/sentry-browser/src/lib/browser.spec.ts create mode 100644 libs/shared/sentry-browser/src/lib/browser.ts create mode 100644 libs/shared/sentry-browser/tsconfig.json create mode 100644 libs/shared/sentry-browser/tsconfig.lib.json create mode 100644 libs/shared/sentry-browser/tsconfig.spec.json create mode 100644 libs/shared/sentry-nest/.eslintrc.json create mode 100644 libs/shared/sentry-nest/README.md create mode 100644 libs/shared/sentry-nest/jest.config.ts create mode 100644 libs/shared/sentry-nest/package.json create mode 100644 libs/shared/sentry-nest/project.json create mode 100644 libs/shared/sentry-nest/src/index.ts create mode 100644 libs/shared/sentry-nest/src/lib/nest.ts create mode 100644 libs/shared/sentry-nest/src/lib/reporting.spec.ts create mode 100644 libs/shared/sentry-nest/src/lib/reporting.ts create mode 100644 libs/shared/sentry-nest/tsconfig.json create mode 100644 libs/shared/sentry-nest/tsconfig.lib.json create mode 100644 libs/shared/sentry-nest/tsconfig.spec.json create mode 100644 libs/shared/sentry-next/.eslintrc.json create mode 100644 libs/shared/sentry-next/README.md create mode 100644 libs/shared/sentry-next/jest.config.ts create mode 100644 libs/shared/sentry-next/package.json create mode 100644 libs/shared/sentry-next/project.json create mode 100644 libs/shared/sentry-next/src/index.ts create mode 100644 libs/shared/sentry-next/src/lib/client.ts create mode 100644 libs/shared/sentry-next/src/lib/server.ts create mode 100644 libs/shared/sentry-next/tsconfig.json create mode 100644 libs/shared/sentry-next/tsconfig.lib.json create mode 100644 libs/shared/sentry-next/tsconfig.spec.json create mode 100644 libs/shared/sentry-node/.eslintrc.json create mode 100644 libs/shared/sentry-node/README.md create mode 100644 libs/shared/sentry-node/jest.config.ts create mode 100644 libs/shared/sentry-node/package.json create mode 100644 libs/shared/sentry-node/project.json create mode 100644 libs/shared/sentry-node/src/index.ts create mode 100644 libs/shared/sentry-node/src/lib/joi-message-overrides.spec.ts create mode 100644 libs/shared/sentry-node/src/lib/joi-message-overrides.ts create mode 100644 libs/shared/sentry-node/src/lib/node.ts create mode 100644 libs/shared/sentry-node/src/lib/report-validation-error.ts create mode 100644 libs/shared/sentry-node/tsconfig.json create mode 100644 libs/shared/sentry-node/tsconfig.lib.json create mode 100644 libs/shared/sentry-node/tsconfig.spec.json create mode 100644 libs/shared/sentry-utils/.eslintrc.json create mode 100644 libs/shared/sentry-utils/README.md create mode 100644 libs/shared/sentry-utils/jest.config.ts create mode 100644 libs/shared/sentry-utils/package.json create mode 100644 libs/shared/sentry-utils/project.json create mode 100644 libs/shared/sentry-utils/src/index.ts create mode 100644 libs/shared/sentry-utils/src/lib/before-send.browser.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/before-send.browser.ts create mode 100644 libs/shared/sentry-utils/src/lib/before-send.server.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/before-send.server.ts create mode 100644 libs/shared/sentry-utils/src/lib/config-builder.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/config-builder.ts create mode 100644 libs/shared/sentry-utils/src/lib/models/pii.ts create mode 100644 libs/shared/sentry-utils/src/lib/models/sentry-config-opts.ts create mode 100644 libs/shared/sentry-utils/src/lib/pii/filter-actions.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/pii/filter-actions.ts create mode 100644 libs/shared/sentry-utils/src/lib/pii/filters.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/pii/filters.ts create mode 100644 libs/shared/sentry-utils/src/lib/sentry.types.ts create mode 100644 libs/shared/sentry-utils/src/lib/utils.spec.ts create mode 100644 libs/shared/sentry-utils/src/lib/utils.ts create mode 100644 libs/shared/sentry-utils/tsconfig.json create mode 100644 libs/shared/sentry-utils/tsconfig.lib.json create mode 100644 libs/shared/sentry-utils/tsconfig.spec.json delete mode 100644 packages/fxa-shared/tracing/exporters/fxa-sentry.ts diff --git a/libs/shared/l10n/src/lib/l10n.utils.ts b/libs/shared/l10n/src/lib/l10n.utils.ts index 301a4c5bee1..0522268b1b3 100644 --- a/libs/shared/l10n/src/lib/l10n.utils.ts +++ b/libs/shared/l10n/src/lib/l10n.utils.ts @@ -17,7 +17,7 @@ import { DEFAULT_LOCALE, EN_GB_LOCALES } from './l10n.constants'; * */ export function parseAcceptLanguage( - acceptLanguage: string, + acceptLanguage?: string, supportedLanguages?: string[] ) { if (!supportedLanguages) { @@ -144,7 +144,7 @@ export function getLocaleFromRequest( * @returns The best fitting locale */ export function determineLocale( - acceptLanguage: string, + acceptLanguage?: string, supportedLanguages?: string[] ) { // Returns languages in order of precedence, so we can just grab the first one. diff --git a/libs/shared/log/src/index.ts b/libs/shared/log/src/index.ts index 31487f586b3..0948d1c2397 100644 --- a/libs/shared/log/src/index.ts +++ b/libs/shared/log/src/index.ts @@ -2,7 +2,7 @@ * 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 { logger } from './lib/logging'; +export * from './lib/logging'; export { monkeyPatchServerLogging } from './lib/monkey-patch'; export { LoggingModule, LOGGER_PROVIDER } from './lib/nest/logging.module'; export type { Logger } from './lib/nest/logging.module'; diff --git a/libs/shared/log/src/lib/logging.ts b/libs/shared/log/src/lib/logging.ts index b2071af21e8..eeef8ff5672 100644 --- a/libs/shared/log/src/lib/logging.ts +++ b/libs/shared/log/src/lib/logging.ts @@ -36,3 +36,10 @@ export const logger = createLogger({ exitOnError: true, ...exceptionHandling, } as LoggerOptions); + +export type ILogger = { + error: (type: string, data: any) => void; + debug: (type: string, data: any) => void; + info: (type: string, data: any) => void; + warn: (type: string, data: any) => void; +}; diff --git a/libs/shared/otel/.eslintrc.json b/libs/shared/otel/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/otel/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/otel/README.md b/libs/shared/otel/README.md new file mode 100644 index 00000000000..b7f6fd6e9aa --- /dev/null +++ b/libs/shared/otel/README.md @@ -0,0 +1,11 @@ +# shared-otel + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-otel` to build the library. + +## Running unit tests + +Run `nx test shared-otel` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/otel/jest.config.ts b/libs/shared/otel/jest.config.ts new file mode 100644 index 00000000000..f75ee1c390b --- /dev/null +++ b/libs/shared/otel/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-otel', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/otel', +}; diff --git a/libs/shared/otel/package.json b/libs/shared/otel/package.json new file mode 100644 index 00000000000..6b14f9dbc47 --- /dev/null +++ b/libs/shared/otel/package.json @@ -0,0 +1,4 @@ +{ + "name": "@fxa/shared/otel", + "version": "0.0.1" +} diff --git a/libs/shared/otel/project.json b/libs/shared/otel/project.json new file mode 100644 index 00000000000..4f517350390 --- /dev/null +++ b/libs/shared/otel/project.json @@ -0,0 +1,29 @@ +{ + "name": "shared-otel", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/otel/src", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/otel", + "main": "libs/shared/otel/src/index.ts", + "tsConfig": "libs/shared/otel/tsconfig.lib.json", + "assets": ["libs/shared/otel/*.md"], + "generatePackageJson": true, + "declaration": true, + "platform": "node" + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/otel/jest.config.ts" + } + } + } +} diff --git a/libs/shared/otel/src/index.ts b/libs/shared/otel/src/index.ts new file mode 100644 index 00000000000..5c71fb405d6 --- /dev/null +++ b/libs/shared/otel/src/index.ts @@ -0,0 +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/. */ + +export * from './lib/config'; +export * from './lib/pii-filters'; +export * from './lib/node-tracing'; diff --git a/libs/shared/otel/src/lib/config.ts b/libs/shared/otel/src/lib/config.ts new file mode 100644 index 00000000000..bf1769541fc --- /dev/null +++ b/libs/shared/otel/src/lib/config.ts @@ -0,0 +1,135 @@ +/* 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 const logType = 'fxa-otel'; +/** + * Options for configuring tracing. + */ +export type TracingOpts = { + batchProcessor: boolean; + clientName: string; + corsUrls: string; + filterPii: boolean; + sampleRate: number; + serviceName: string; + + console?: { + enabled: boolean; + }; + gcp?: { + enabled: boolean; + }; + otel?: { + enabled: boolean; + url: string; + concurrencyLimit: number; + }; +}; + +/** Default convict config for node tracing */ +export const tracingConfig = { + clientName: { + default: '', + doc: 'The name of client being traced.', + env: 'TRACING_CLIENT_NAME', + format: String, + }, + batchProcessor: { + default: true, + doc: 'Indicates if batch processing should be used. Batch processing is better for production environments.', + env: 'TRACING_BATCH_PROCESSING', + format: Boolean, + }, + corsUrls: { + default: 'http://localhost:\\d*/', + doc: 'A regex to allow tracing of cors requests', + env: 'TRACING_CORS_URLS', + format: String, + }, + filterPii: { + default: true, + doc: 'Enables filtering PII in Console traces.', + env: 'TRACING_FILTER_PII', + format: Boolean, + }, + sampleRate: { + default: 0, + doc: 'A number between 0 and 1 that indicates the rate at which to sample. 1 will capture all traces. .5 would capture half the traces, and 0 would capture no traces.', + env: 'TRACING_SAMPLE_RATE', + format: Number, + }, + serviceName: { + default: '', + doc: 'The name of service being traced.', + env: 'TRACING_SERVICE_NAME', + format: String, + }, + console: { + enabled: { + default: false, + doc: 'Trace report to the console', + env: 'TRACING_CONSOLE_EXPORTER_ENABLED', + format: Boolean, + }, + }, + gcp: { + enabled: { + default: false, + doc: 'Traces report to google cloud tracing. This should be turned on in the wild, but is discouraged for local development.', + env: 'TRACING_GCP_EXPORTER_ENABLED', + format: Boolean, + }, + }, + otel: { + enabled: { + default: false, + doc: 'Traces report to the otel. This is only applicable for local development.', + env: 'TRACING_OTEL_EXPORTER_ENABLED', + format: Boolean, + }, + url: { + default: 'http://localhost:4318/v1/traces', + doc: 'Open telemetry collector url', + env: 'TRACING_OTEL_URL', + format: String, + }, + concurrencyLimit: { + default: 100, + doc: 'Max amount of concurrency', + env: 'TRACING_OTEL_CONCURRENCY_LIMIT', + format: Number, + }, + }, +}; + +export function checkServiceName(opts: Pick) { + if (!opts.serviceName) { + throw new Error('Missing config. serviceName must be defined!'); + } +} + +export function checkSampleRate(opts: Pick) { + if ( + opts.sampleRate == null || + Number.isNaN(opts.sampleRate) || + opts.sampleRate < 0 || + opts.sampleRate > 1 + ) { + throw new Error( + `Invalid config. sampleRate must be a number between 0 and 1, but was ${opts.sampleRate}.` + ); + } +} + +export function checkClientName(opts: Pick) { + if (!opts.clientName) { + throw new Error('Missing config. clientName must be defined!'); + } +} + +export function someModesEnabled( + opts: Pick +) { + return opts.otel?.enabled || opts.gcp?.enabled || opts.console?.enabled; +} diff --git a/libs/shared/otel/src/lib/exporters/exporters.ts b/libs/shared/otel/src/lib/exporters/exporters.ts new file mode 100644 index 00000000000..4cca38c5fa4 --- /dev/null +++ b/libs/shared/otel/src/lib/exporters/exporters.ts @@ -0,0 +1,25 @@ +/* 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 { TracingOpts } from '../config'; +import { + NodeTracerProvider, + BatchSpanProcessor, + SimpleSpanProcessor, + SpanExporter, +} from '@opentelemetry/sdk-trace-node'; + +export function addExporter( + opts: TracingOpts, + provider: NodeTracerProvider, + exporter: SpanExporter +) { + const processor = opts.batchProcessor + ? new BatchSpanProcessor(exporter) + : new SimpleSpanProcessor(exporter); + provider.addSpanProcessor(processor); + + provider.addSpanProcessor(processor); + return processor; +} diff --git a/libs/shared/otel/src/lib/exporters/fxa-console.ts b/libs/shared/otel/src/lib/exporters/fxa-console.ts new file mode 100644 index 00000000000..571b19913fb --- /dev/null +++ b/libs/shared/otel/src/lib/exporters/fxa-console.ts @@ -0,0 +1,48 @@ +/* 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 { ExportResult } from '@opentelemetry/core'; +import { + BasicTracerProvider, + ConsoleSpanExporter, + ReadableSpan, +} from '@opentelemetry/sdk-trace-node'; +import { logType, TracingOpts } from '../config'; +import { TracingPiiFilter } from '../pii-filters'; +import { addExporter } from './exporters'; +import { checkDuration } from './util'; +import { ILogger } from '@fxa/shared/log'; + +/** Console Exporter exporter customized for FxA */ +export class FxaConsoleSpanExporter extends ConsoleSpanExporter { + constructor(protected readonly filter?: TracingPiiFilter) { + super(); + } + + override export( + spans: ReadableSpan[], + resultCallback: (result: ExportResult) => void + ) { + spans.forEach((x) => { + checkDuration(x); + this.filter?.filter(x); + }); + return super.export(spans, resultCallback); + } +} + +export function addConsoleExporter( + opts: TracingOpts, + provider: BasicTracerProvider, + filter?: TracingPiiFilter, + logger?: ILogger +) { + if (!opts.console?.enabled) { + return; + } + logger?.debug(logType, 'Adding Console Exporter'); + const exporter = new FxaConsoleSpanExporter(filter); + addExporter(opts, provider, exporter); + return exporter; +} diff --git a/libs/shared/otel/src/lib/exporters/fxa-gcp.ts b/libs/shared/otel/src/lib/exporters/fxa-gcp.ts new file mode 100644 index 00000000000..1c76951061f --- /dev/null +++ b/libs/shared/otel/src/lib/exporters/fxa-gcp.ts @@ -0,0 +1,56 @@ +/* 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 { + TraceExporter as GcpTraceExporter, + TraceExporterOptions as GcpTraceExporterOptions, +} from '@google-cloud/opentelemetry-cloud-trace-exporter'; +import { ExportResult } from '@opentelemetry/core'; +import { + BasicTracerProvider, + ReadableSpan, +} from '@opentelemetry/sdk-trace-node'; +import { TracingOpts, logType } from '../config'; +import { TracingPiiFilter } from '../pii-filters'; +import { addExporter } from './exporters'; +import { checkDuration } from './util'; +import { ILogger } from '@fxa/shared/log'; + +/** Gcp exporter customized for FxA */ + +export class FxaGcpTraceExporter extends GcpTraceExporter { + constructor( + protected readonly filter?: TracingPiiFilter, + config?: GcpTraceExporterOptions + ) { + super(config); + } + + override export( + spans: ReadableSpan[], + resultCallback: (result: ExportResult) => void + ) { + spans.forEach((x) => { + checkDuration(x); + this.filter?.filter(x); + }); + return super.export(spans, resultCallback); + } +} + +export function addGcpTraceExporter( + opts: TracingOpts, + provider: BasicTracerProvider, + filter?: TracingPiiFilter, + logger?: ILogger +) { + if (!opts.gcp?.enabled) { + return; + } + + logger?.debug(logType, { msg: 'Adding Gcp Trace Exporter' }); + const exporter = new FxaGcpTraceExporter(filter); + addExporter(opts, provider, exporter); + return exporter; +} diff --git a/libs/shared/otel/src/lib/exporters/fxa-otlp.ts b/libs/shared/otel/src/lib/exporters/fxa-otlp.ts new file mode 100644 index 00000000000..d80f3bd8acc --- /dev/null +++ b/libs/shared/otel/src/lib/exporters/fxa-otlp.ts @@ -0,0 +1,74 @@ +/* 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 { ExportResult } from '@opentelemetry/core'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; +import { + BasicTracerProvider, + ReadableSpan, +} from '@opentelemetry/sdk-trace-node'; +import { TracingOpts, logType } from '../config'; +import { TracingPiiFilter } from '../pii-filters'; +import { addExporter } from './exporters'; +import { checkDuration } from './util'; +import { ILogger } from '@fxa/shared/log'; + +export type FxaOtlpTracingHeaders = { + flowid?: string; + traceparent?: string; + tracestate?: string; +}; + +/** OTLP exporter customized for FxA */ +export class FxaOtlpWebExporter extends OTLPTraceExporter { + constructor( + protected readonly filter?: TracingPiiFilter, + config?: OTLPExporterConfigBase, + protected readonly logger?: ILogger + ) { + super(config); + } + + override export( + spans: ReadableSpan[], + resultCallback: (result: ExportResult) => void + ) { + spans.forEach((x) => { + checkDuration(x); + this.filter?.filter(x); + }); + super.export(spans, (result) => { + if (result.error) { + this.logger?.error(logType, result.error); + } + resultCallback(result); + }); + } +} + +export function addOtlpTraceExporter( + opts: TracingOpts, + provider: BasicTracerProvider, + headers?: FxaOtlpTracingHeaders, + filter?: TracingPiiFilter, + logger?: ILogger +) { + if (!opts.otel?.enabled) { + return; + } + + logger?.debug(logType, { + msg: 'Adding Otlp Trace Exporter ', + url: opts.otel?.url, + }); + const config = { + url: opts.otel?.url, + headers, + concurrencyLimit: opts.otel?.concurrencyLimit, + }; + const exporter = new FxaOtlpWebExporter(filter, config, logger); + addExporter(opts, provider, exporter); + return exporter; +} diff --git a/libs/shared/otel/src/lib/exporters/util.ts b/libs/shared/otel/src/lib/exporters/util.ts new file mode 100644 index 00000000000..cd077d2da2f --- /dev/null +++ b/libs/shared/otel/src/lib/exporters/util.ts @@ -0,0 +1,45 @@ +/* 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 { ReadableSpan } from '@opentelemetry/sdk-trace-node'; + +/** + * Checks, fixes, and flags bad duration state on spans. + * @param span + */ +export function checkDuration( + span: Pick +) { + // Temporary Hack: There appears to be bug in the client side instrumentation code + // that results in negative durations. These negative durations end up being + // coerced into unsigned ints creating very odd and usually large values, which + // obscures the entire trace's timing. To work around this, we will zero out these + // durations and set an incorrect.duration flag. + // + // Note, that duration is of type HrTime. See Type definition to understand its + // format. + // + + let adjusted = false; + // Adjust epoch + if (span.startTime[0] > span.endTime[0]) { + span.startTime[0] = span.endTime[0]; + adjusted = true; + } + + // Adjust nano seconds + if ( + span.startTime[0] === span.endTime[0] && + span.startTime[1] > span.endTime[1] + ) { + span.endTime[1] = span.startTime[1]; + adjusted = true; + } + + if (adjusted) { + span.duration[0] = span.endTime[0] - span.startTime[0]; + span.duration[1] = span.endTime[1] - span.startTime[1]; + span.attributes['incorrect.duration'] = 'true'; + } +} diff --git a/libs/shared/otel/src/lib/node-tracing.spec.ts b/libs/shared/otel/src/lib/node-tracing.spec.ts new file mode 100644 index 00000000000..c54c47a0d17 --- /dev/null +++ b/libs/shared/otel/src/lib/node-tracing.spec.ts @@ -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/. */ + +import { initTracing } from './node-tracing'; + +describe('Initializes node tracing', () => { + it('flags when code and errno are present', () => { + const nodeTracing = initTracing( + { + batchProcessor: false, + clientName: 'test', + corsUrls: '', + filterPii: false, + sampleRate: 0, + serviceName: 'test', + console: { + enabled: true, + }, + }, + console + ); + + expect(nodeTracing).toBeDefined(); + }); +}); diff --git a/libs/shared/otel/src/lib/node-tracing.ts b/libs/shared/otel/src/lib/node-tracing.ts new file mode 100644 index 00000000000..5a5eb9e478e --- /dev/null +++ b/libs/shared/otel/src/lib/node-tracing.ts @@ -0,0 +1,167 @@ +/* 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 api from '@opentelemetry/api'; +import { suppressTracing } from '@opentelemetry/core'; +import { ILogger } from '@fxa/shared/log'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { + checkSampleRate, + checkServiceName, + TracingOpts, + logType, +} from './config'; +import { addConsoleExporter } from './exporters/fxa-console'; +import { addGcpTraceExporter } from './exporters/fxa-gcp'; +import { addOtlpTraceExporter } from './exporters/fxa-otlp'; +import { createPiiFilter } from './pii-filters'; +import { createNodeProvider } from './providers/node-provider'; + +export const TRACER_NAME = 'fxa-tracer'; + +/** + * Responsible for initializing node tracing from a config object. This uses the auto instrumentation feature + * which tries to add as much instrumentation as possible. See the 'supported instrumentations section at + * https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node for more info. + */ +export class NodeTracingInitializer { + protected provider: NodeTracerProvider; + + constructor( + protected readonly opts: TracingOpts, + protected readonly logger?: ILogger + ) { + // Error out if certain options are invalid + checkServiceName(this.opts); + checkSampleRate(this.opts); + + this.provider = createNodeProvider(this.opts); + + const filter = createPiiFilter(!!this.opts?.filterPii, this.logger); + addGcpTraceExporter(opts, this.provider, filter, this.logger); + addOtlpTraceExporter(opts, this.provider, undefined, filter, this.logger); + addConsoleExporter(opts, this.provider, filter); + + this.register(); + } + + protected register() { + registerInstrumentations({ + instrumentations: [ + // ...extraInstrumentations, + getNodeAutoInstrumentations({ + // These instrumentations added a lot of unnecessary noise + '@opentelemetry/instrumentation-dns': { + enabled: false, + }, + '@opentelemetry/instrumentation-net': { + enabled: false, + }, + '@opentelemetry/instrumentation-fs': { + enabled: false, + }, + '@opentelemetry/instrumentation-aws-lambda': { + enabled: false, + }, + }), + ], + }); + this.provider.register(); + } + + public startSpan(name: string, action: () => void) { + return this.provider.getTracer(TRACER_NAME).startActiveSpan(name, action); + } + + /** Gets current traceId */ + public getTraceId() { + const currentSpan = api.trace.getSpan(api.context.active()); + if (currentSpan) { + return currentSpan.spanContext().traceId; + } + return null; + } + + public getTraceParentId() { + const tracer = this.provider.getTracer('fxa'); + const span = tracer.startSpan('client-inject'); + const version = '00'; + const spanContext = span.spanContext(); + let sampleDecision = '00'; + if (Math.random() <= this.opts.sampleRate) { + sampleDecision = '01'; + } + const parentId = `${version}-${spanContext.traceId}-${spanContext.spanId}-${sampleDecision}`; + span.end(); + return parentId; + } +} + +/** Singleton */ +let nodeTracing: NodeTracingInitializer | undefined; + +/** Gets active trace parent id */ +export function getTraceParentId() { + if (nodeTracing == null) { + return '00-0-0-00'; + } + return nodeTracing.getTraceParentId(); +} + +/** Initializes tracing in node context */ +let initialized = false; +export function initTracing(opts: TracingOpts, logger: ILogger) { + if (initialized) { + logger.warn(logType, { msg: 'Tracing already initialized!' }); + return; + } + initialized = true; + + if (nodeTracing != null) { + logger.debug(logType, { + msg: 'Trace initialization skipped. Tracing already initialized, ignoring new opts.', + }); + return nodeTracing; + } + + if (!opts.otel?.enabled && !opts.gcp?.enabled && !opts.console?.enabled) { + logger.debug(logType, { + msg: 'Trace initialization skipped. No exporters configured. Enable gcp, otel or console to activate tracing.', + }); + return; + } + + try { + nodeTracing = new NodeTracingInitializer(opts, logger); + logger.info(logType, { msg: 'Trace initialized succeeded!' }); + } catch (err) { + logger.error(logType, { + msg: `Trace initialization failed: ${err.message}`, + }); + } + return nodeTracing; +} + +/** Get the current instance of the node tracing provider. */ +export function getCurrent() { + return nodeTracing; +} + +/** Indicates that tracing has been initialized. */ +export function isInitialized() { + return !!nodeTracing; +} + +/** Suppresses trace capture on the current context */ +export function suppressTrace(action: () => any) { + const currentCtx = api.context.active(); + return api.context.with(suppressTracing(currentCtx), action); +} + +/** Resets the current tracing instance. Use only for testing purposes. */ +export function reset() { + nodeTracing = undefined; +} diff --git a/libs/shared/otel/src/lib/pii-filters.ts b/libs/shared/otel/src/lib/pii-filters.ts new file mode 100644 index 00000000000..afb2acd6664 --- /dev/null +++ b/libs/shared/otel/src/lib/pii-filters.ts @@ -0,0 +1,80 @@ +/* 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 { ILogger } from '@fxa/shared/log'; +import { + PiiData, + CommonPiiActions, + FilterBase, +} from '@fxa/shared/sentry-utils'; + +/** Matches attribute names that need to be filtered. */ +const reTargetPiiAttributes = /^(db|http)\./; + +/** + * PiiFilter specifically for scrubbing open telemetry traces. + */ +export class TracingPiiFilter extends FilterBase { + /** + * Creates new PII Filter for tracing + * @param logger - optional logger + */ + constructor(logger?: ILogger) { + super( + [ + CommonPiiActions.breadthFilter, + CommonPiiActions.depthFilter, + CommonPiiActions.piiKeys, + CommonPiiActions.emailValues, + CommonPiiActions.tokenValues, + CommonPiiActions.ipV4Values, + CommonPiiActions.ipV6Values, + CommonPiiActions.urlUsernamePassword, + ], + logger + ); + } + + /** + * Filter traces. + * @param data - Data to filter. + */ + filter(data: PiiData): PiiData { + try { + if (typeof data === 'object' && data?.['attributes']) { + for (const key of Object.keys(data['attributes'])) { + if (reTargetPiiAttributes.test(key)) { + data['attributes'][key] = this.applyFilters( + data['attributes'][key] + ); + } + } + } + } catch (err) { + // Note, we have to throw the error, since the trace could contain PII if + // the routine doesn't exist cleanly. We will log this, so there's some way + // to track the problem down if we notice missing spans. + this.logger?.error('pii-trace-filter', err); + throw err; + } + + return data; + } +} + +/** Singleton */ +let piiFilter: TracingPiiFilter; + +/** Creates a PII filter for tracing. This behaves as a singleton. */ +export function createPiiFilter(enabled: boolean, logger?: ILogger) { + if (!enabled) { + return; + } + + if (!piiFilter) { + piiFilter = new TracingPiiFilter(logger); + } + + return piiFilter; +} diff --git a/libs/shared/otel/src/lib/providers/node-provider.ts b/libs/shared/otel/src/lib/providers/node-provider.ts new file mode 100644 index 00000000000..f636f80f6f0 --- /dev/null +++ b/libs/shared/otel/src/lib/providers/node-provider.ts @@ -0,0 +1,23 @@ +/* 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 { Resource } from '@opentelemetry/resources'; +import { + ParentBasedSampler, + TraceIdRatioBasedSampler, +} from '@opentelemetry/sdk-trace-base'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { TracingOpts } from '../config'; + +export function createNodeProvider(opts: TracingOpts) { + return new NodeTracerProvider({ + sampler: new ParentBasedSampler({ + root: new TraceIdRatioBasedSampler(opts.sampleRate), + }), + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: opts.serviceName, + }), + }); +} diff --git a/libs/shared/otel/src/lib/providers/web-provider.ts b/libs/shared/otel/src/lib/providers/web-provider.ts new file mode 100644 index 00000000000..3ad6d953d45 --- /dev/null +++ b/libs/shared/otel/src/lib/providers/web-provider.ts @@ -0,0 +1,23 @@ +/* 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 { Resource } from '@opentelemetry/resources'; +import { + ParentBasedSampler, + TraceIdRatioBasedSampler, +} from '@opentelemetry/sdk-trace-base'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { TracingOpts } from '../config'; + +export function createWebProvider(opts: TracingOpts) { + return new WebTracerProvider({ + sampler: new ParentBasedSampler({ + root: new TraceIdRatioBasedSampler(opts.sampleRate), + }), + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: opts.clientName, + }), + }); +} diff --git a/libs/shared/otel/tsconfig.json b/libs/shared/otel/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/otel/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/otel/tsconfig.lib.json b/libs/shared/otel/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/otel/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/otel/tsconfig.spec.json b/libs/shared/otel/tsconfig.spec.json new file mode 100644 index 00000000000..69a251f328c --- /dev/null +++ b/libs/shared/otel/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry-browser/.eslintrc.json b/libs/shared/sentry-browser/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/sentry-browser/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/sentry-browser/README.md b/libs/shared/sentry-browser/README.md new file mode 100644 index 00000000000..81f44a43b98 --- /dev/null +++ b/libs/shared/sentry-browser/README.md @@ -0,0 +1,11 @@ +# shared-sentry-browser + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-sentry-browser` to build the library. + +## Running unit tests + +Run `nx test shared-sentry-browser` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/sentry-browser/jest.config.ts b/libs/shared/sentry-browser/jest.config.ts new file mode 100644 index 00000000000..eda9ca5c1fe --- /dev/null +++ b/libs/shared/sentry-browser/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-sentry-browser', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/sentry-browser', +}; diff --git a/libs/shared/sentry-browser/package.json b/libs/shared/sentry-browser/package.json new file mode 100644 index 00000000000..01e83c4837f --- /dev/null +++ b/libs/shared/sentry-browser/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fxa/shared/sentry-browser", + "version": "0.0.1", + "dependencies": {}, + "type": "commonjs", + "main": "./index.cjs", + "private": true +} diff --git a/libs/shared/sentry-browser/project.json b/libs/shared/sentry-browser/project.json new file mode 100644 index 00000000000..4679def6b95 --- /dev/null +++ b/libs/shared/sentry-browser/project.json @@ -0,0 +1,28 @@ +{ + "name": "shared-sentry-browser", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/sentry-browser/src", + "projectType": "library", + "tags": ["scope:shared:lib"], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/sentry-browser", + "main": "libs/shared/sentry-browser/src/index.ts", + "tsConfig": "libs/shared/sentry-browser/tsconfig.lib.json", + "assets": ["libs/shared/sentry-browser/*.md"], + "generatePackageJson": true, + "format": ["cjs"] + } + }, + "test-unit": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/sentry-browser/jest.config.ts" + } + } + } +} diff --git a/libs/shared/sentry-browser/src/index.ts b/libs/shared/sentry-browser/src/index.ts new file mode 100644 index 00000000000..c1006a1300c --- /dev/null +++ b/libs/shared/sentry-browser/src/index.ts @@ -0,0 +1 @@ +export * from './lib/browser'; diff --git a/libs/shared/sentry-browser/src/lib/browser.spec.ts b/libs/shared/sentry-browser/src/lib/browser.spec.ts new file mode 100644 index 00000000000..aa7c7d61d0f --- /dev/null +++ b/libs/shared/sentry-browser/src/lib/browser.spec.ts @@ -0,0 +1,45 @@ +/* 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 'jsdom-global/register'; +import * as Sentry from '@sentry/browser'; +import { enableSentry, SentryConfigOpts } from '@fxa/shared/sentry-utils'; +import { captureException, initSentry } from './browser'; + +const config: SentryConfigOpts = { + release: 'v0.0.0', + sentry: { + dsn: 'https://public:private@host:8080/1', + env: 'test', + clientName: 'fxa-shared-testing', + sampleRate: 0, + }, +}; + +describe('sentry/browser', () => { + beforeEach(() => { + enableSentry(); + }); + + describe('initSentry', () => { + it('initializes', () => { + const spy = jest.spyOn(Sentry, 'init'); + initSentry(config); + expect(spy).toBeCalledTimes(1); + + spy.mockReset(); + spy.mockRestore(); + }); + }); + + describe('captureException', () => { + it('calls Sentry.captureException', () => { + const spy = jest.spyOn(Sentry, 'captureException'); + captureException(new Error('testo')); + expect(spy).toHaveBeenCalled(); + + spy.mockReset(); + spy.mockRestore(); + }); + }); +}); diff --git a/libs/shared/sentry-browser/src/lib/browser.ts b/libs/shared/sentry-browser/src/lib/browser.ts new file mode 100644 index 00000000000..2298b8586ee --- /dev/null +++ b/libs/shared/sentry-browser/src/lib/browser.ts @@ -0,0 +1,68 @@ +/* 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/browser'; +import { + SentryConfigOpts, + buildSentryConfig, + Logger, + beforeSendBrowser, + disableSentry, + isSentryEnabled, +} from '@fxa/shared/sentry-utils'; + +/** + * Exception fields that are imported as tags + */ +const EXCEPTION_TAGS = ['code', 'context', 'errno', 'namespace', 'status']; + +export function captureException(err: Error) { + if (!isSentryEnabled()) { + return; + } + + Sentry.withScope((scope: Sentry.Scope) => { + EXCEPTION_TAGS.forEach((tagName) => { + if (tagName in err) { + scope.setTag( + tagName, + ( + err as { + [key: string]: any; + } + )[tagName] + ); + } + }); + Sentry.captureException(err); + }); +} + +export function initSentry(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. + disableSentry(); + + const opts = buildSentryConfig(config, log); + try { + Sentry.init({ + ...opts, + beforeSend: function (event: Sentry.ErrorEvent, hint: Sentry.EventHint) { + return beforeSendBrowser(opts, event, hint); + }, + }); + } catch (e) { + log.error(e); + } +} diff --git a/libs/shared/sentry-browser/tsconfig.json b/libs/shared/sentry-browser/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/sentry-browser/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/sentry-browser/tsconfig.lib.json b/libs/shared/sentry-browser/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/sentry-browser/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/sentry-browser/tsconfig.spec.json b/libs/shared/sentry-browser/tsconfig.spec.json new file mode 100644 index 00000000000..d41891c0a50 --- /dev/null +++ b/libs/shared/sentry-browser/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry-nest/.eslintrc.json b/libs/shared/sentry-nest/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/sentry-nest/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/sentry-nest/README.md b/libs/shared/sentry-nest/README.md new file mode 100644 index 00000000000..da2114cc5c7 --- /dev/null +++ b/libs/shared/sentry-nest/README.md @@ -0,0 +1,11 @@ +# shared-sentry-nest + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-sentry-nest` to build the library. + +## Running unit tests + +Run `nx test shared-sentry-nest` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/sentry-nest/jest.config.ts b/libs/shared/sentry-nest/jest.config.ts new file mode 100644 index 00000000000..6e3b5fbb213 --- /dev/null +++ b/libs/shared/sentry-nest/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-sentry-nest', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/sentry-nest', +}; diff --git a/libs/shared/sentry-nest/package.json b/libs/shared/sentry-nest/package.json new file mode 100644 index 00000000000..2edc7f50612 --- /dev/null +++ b/libs/shared/sentry-nest/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fxa/shared/sentry-nest", + "version": "0.0.1", + "dependencies": {}, + "type": "commonjs", + "main": "./index.cjs", + "private": true +} diff --git a/libs/shared/sentry-nest/project.json b/libs/shared/sentry-nest/project.json new file mode 100644 index 00000000000..df4a45b9a9b --- /dev/null +++ b/libs/shared/sentry-nest/project.json @@ -0,0 +1,28 @@ +{ + "name": "shared-sentry-nest", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/sentry-nest/src", + "projectType": "library", + "tags": ["scope:shared:lib"], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/sentry-nest", + "main": "libs/shared/sentry-nest/src/index.ts", + "tsConfig": "libs/shared/sentry-nest/tsconfig.lib.json", + "assets": ["libs/shared/sentry-nest/*.md"], + "generatePackageJson": true, + "format": ["cjs"] + } + }, + "test-unit": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/sentry-nest/jest.config.ts" + } + } + } +} diff --git a/libs/shared/sentry-nest/src/index.ts b/libs/shared/sentry-nest/src/index.ts new file mode 100644 index 00000000000..b4ff6b456d4 --- /dev/null +++ b/libs/shared/sentry-nest/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/nest'; +export * from './lib/reporting'; diff --git a/libs/shared/sentry-nest/src/lib/nest.ts b/libs/shared/sentry-nest/src/lib/nest.ts new file mode 100644 index 00000000000..00a5b9b1db5 --- /dev/null +++ b/libs/shared/sentry-nest/src/lib/nest.ts @@ -0,0 +1,50 @@ +/* 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 { ErrorEvent } from '@sentry/types'; +import * as Sentry from '@sentry/nestjs'; +import { + beforeSendServer, + buildSentryConfig, + InitSentryOpts, + Logger, +} from '@fxa/shared/sentry-utils'; + +/** + * There are multiple sentry implementations. For example, @sentry/node and @sentry/nestjs. + * Depending on the application context. + */ +export function initSentry(config: InitSentryOpts, log: Logger) { + if (!config?.sentry?.dsn) { + log.error('No Sentry dsn provided. Cannot start sentry'); + return; + } + + const opts = buildSentryConfig(config, log); + + const integrations = [ + Sentry.extraErrorDataIntegration({ depth: 5 }), + + // Custom Integrations + ...(config.integrations || []), + ]; + + try { + Sentry.init({ + // Defaults Options + normalizeDepth: 6, + maxValueLength: 500, + + // Custom Options + integrations, + beforeSend: (event: ErrorEvent, hint: any) => { + return beforeSendServer(config, event, hint); + }, + ...opts, + }); + } catch (e) { + log.debug('init-sentry', { msg: 'Issue initializing sentry!' }); + log.error('init-sentry', e); + } +} diff --git a/libs/shared/sentry-nest/src/lib/reporting.spec.ts b/libs/shared/sentry-nest/src/lib/reporting.spec.ts new file mode 100644 index 00000000000..3298f70b393 --- /dev/null +++ b/libs/shared/sentry-nest/src/lib/reporting.spec.ts @@ -0,0 +1,99 @@ +/* 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 { ignoreError, isAuthServerError } from './reporting'; +import { GraphQLError } from 'graphql'; +import { HttpException } from '@nestjs/common'; + +describe('detects auth server error', () => { + it('flags when code and errno are present', () => { + const result = isAuthServerError({ + name: 'foo', + message: 'bar', + extensions: { + code: 401, + errno: 101, + }, + }); + + expect(result).toBeTruthy(); + }); + + it('does not flag when code is missing', () => { + const result = isAuthServerError({ + name: 'foo', + message: 'bar', + extensions: { + errno: 101, + }, + }); + + expect(result).toBeFalsy(); + }); + + it('does not flag when errno is missing', () => { + const result = isAuthServerError({ + name: 'foo', + message: 'bar', + extensions: { + code: 500, + }, + }); + + expect(result).toBeFalsy(); + }); + + it('does not flag general errors', () => { + const result = isAuthServerError({ + name: 'foo', + message: 'bar', + }); + + expect(result).toBeFalsy(); + }); +}); +describe('error ignore policies', () => { + it('should ignore known auth server errors', () => { + expect( + ignoreError({ + extensions: { + code: 100, + errno: 100, + }, + }) + ).toBeTruthy(); + }); + + it('should ignore apollo errors', () => { + // Apollo errors should be sent to clients and we don't report them. + const err = new GraphQLError('BOOM', { + extensions: { + code: 'BAD_REQUEST', + }, + }); + expect(ignoreError(err)).toBeTruthy(); + }); + + it('should ignore non 500 errors', () => { + // Non-500s are expected responses for clients and we don't report them. + expect(ignoreError(new HttpException('Not Found', 404))).toBeTruthy(); + }); + + it('should ignore errors with originalError specified', () => { + // This means we are dealing with a wrapped error, so don't report it. + expect(ignoreError({ originalError: { status: 500 } })).toBeTruthy(); + }); + + it('should not ignore general errors', () => { + expect(ignoreError(new Error('BOOM'))).toBeFalsy(); + }); + + it('should not ignore things that look like internal server error', () => { + expect(ignoreError(new HttpException('BOOM', 500))).toBeFalsy(); + }); + + it('should not ignore an unknown error type', () => { + expect(ignoreError({ not: 'typical' })).toBeFalsy(); + }); +}); diff --git a/libs/shared/sentry-nest/src/lib/reporting.ts b/libs/shared/sentry-nest/src/lib/reporting.ts new file mode 100644 index 00000000000..0e711691801 --- /dev/null +++ b/libs/shared/sentry-nest/src/lib/reporting.ts @@ -0,0 +1,157 @@ +import * as Sentry from '@sentry/nestjs'; +import { ExecutionContext, HttpException } from '@nestjs/common'; +import { GqlContextType, GqlExecutionContext } from '@nestjs/graphql'; +import { ApolloServerErrorCode } from '@apollo/server/errors'; + +import { CommonPiiActions, SqsMessageFilter } from '@fxa/shared/sentry-utils'; +import { GraphQLError } from 'graphql'; +import { SQS } from 'aws-sdk'; +import { Request } from 'express'; + +export interface ExtraContext { + name: string; + fieldData: Record; +} + +const sqsMessageFilter = new SqsMessageFilter([ + CommonPiiActions.emailValues, + CommonPiiActions.tokenValues, +]); + +/** + * Capture a SQS Error to Sentry with additional context. + * + * @param err Error object to capture. + * @param message SQS Message to include with error. + */ +export function captureSqsError(err: Error, message?: SQS.Message): void { + Sentry.withScope((scope) => { + if (message?.Body) { + message = sqsMessageFilter.filter(message); + scope.setContext('SQS Message', message as Record); + } + Sentry.captureException(err); + }); +} + +/** + * Determine if an error is an ApolloError. + * Prior to GQL 16.8 and apollo-server 4.9.3, we used ApolloError from apollo-server. + * Now, we populate fields on GraphQL error to mimic the previous state of ApolloError. + */ +export function isApolloError(err: Error): boolean { + if (err instanceof GraphQLError) { + const code = err.extensions?.['code']; + if (typeof code === 'string') { + return Object.keys(ApolloServerErrorCode).includes(code); + } + } + return false; +} + +/** Indicates if error should be sent to Sentry */ +export function ignoreError(err: any): boolean { + return ( + isAuthServerError(err) || + isApolloError(err) || + isOriginallyHttpError(err) || + (isHttpException(err) && !isInternalServerError(err)) + ); +} + +/** + * Determine if an error originates from auth-server. Auth server responds with error + * codes and numbers, and client applications handle these states accordingly. These + * responses are not considered unhandled error states, and therefore should not + * be reported on. + * @param error - The error that has occurred + * @returns true if errors states appears to be a known auth server error + */ +export function isAuthServerError( + error: Error & { extensions?: { errno?: number; code?: number } } +): boolean { + return ( + typeof error.extensions?.code === 'number' && + typeof error.extensions?.errno === 'number' + ); +} + +export function isHttpException(err: any) { + return ( + err instanceof HttpException || err.constructor.name === 'HttpException' + ); +} + +export function isInternalServerError(err: Error) { + try { + if ((err as HttpException).getStatus() >= 500) { + return true; + } + } catch {} + + return false; +} + +export function isOriginallyHttpError( + error: Error & { originalError?: { status: number } } +): boolean { + return typeof error?.originalError?.status === 'number'; +} + +/** + * Report an exception with request and additional optional context objects. + * + * @param exception + * @param excContexts List of additional exception context objects to capture. + * @param request A request object if available. + */ +export function reportRequestException( + exception: Error & { reported?: boolean; status?: number; response?: any }, + excContexts: ExtraContext[] = [], + request?: Request +) { + // Don't report already reported exceptions + if (exception.reported) { + return; + } + + Sentry.withScope((scope: Sentry.Scope) => { + scope.addEventProcessor((event) => { + if (request) { + event.request = Sentry.extractRequestData(request); + event.level = 'error'; + return event; + } + return null; + }); + for (const ctx of excContexts) { + scope.setContext(ctx.name, ctx.fieldData); + } + + Sentry.captureException(exception); + exception.reported = true; + }); +} + +// TODO: Dead code? +export function processException(context: ExecutionContext, exception: Error) { + // First determine what type of a request this is + let request: Request | undefined; + let gqlExec: GqlExecutionContext | undefined; + if (context.getType() === 'http') { + request = context.switchToHttp().getRequest(); + } else if (context.getType() === 'graphql') { + gqlExec = GqlExecutionContext.create(context); + request = gqlExec.getContext().req; + } + const excContexts: ExtraContext[] = []; + if (gqlExec) { + const info = gqlExec.getInfo(); + excContexts.push({ + name: 'graphql', + fieldData: { fieldName: info.fieldName, path: info.path }, + }); + } + + reportRequestException(exception, excContexts, request); +} diff --git a/libs/shared/sentry-nest/tsconfig.json b/libs/shared/sentry-nest/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/sentry-nest/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/sentry-nest/tsconfig.lib.json b/libs/shared/sentry-nest/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/sentry-nest/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/sentry-nest/tsconfig.spec.json b/libs/shared/sentry-nest/tsconfig.spec.json new file mode 100644 index 00000000000..69a251f328c --- /dev/null +++ b/libs/shared/sentry-nest/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry-next/.eslintrc.json b/libs/shared/sentry-next/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/sentry-next/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/sentry-next/README.md b/libs/shared/sentry-next/README.md new file mode 100644 index 00000000000..080a928708c --- /dev/null +++ b/libs/shared/sentry-next/README.md @@ -0,0 +1,11 @@ +# shared-sentry-next + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-sentry-next` to build the library. + +## Running unit tests + +Run `nx test shared-sentry-next` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/sentry-next/jest.config.ts b/libs/shared/sentry-next/jest.config.ts new file mode 100644 index 00000000000..c5fbbb50f96 --- /dev/null +++ b/libs/shared/sentry-next/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-sentry-next', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/sentry-next', +}; diff --git a/libs/shared/sentry-next/package.json b/libs/shared/sentry-next/package.json new file mode 100644 index 00000000000..3a7fa353c82 --- /dev/null +++ b/libs/shared/sentry-next/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fxa/shared/sentry-next", + "version": "0.0.1", + "dependencies": {}, + "type": "commonjs", + "main": "./index.cjs", + "private": true +} diff --git a/libs/shared/sentry-next/project.json b/libs/shared/sentry-next/project.json new file mode 100644 index 00000000000..74420c14936 --- /dev/null +++ b/libs/shared/sentry-next/project.json @@ -0,0 +1,28 @@ +{ + "name": "shared-sentry-next", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/sentry-next/src", + "projectType": "library", + "tags": ["scope:shared:lib"], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/sentry-next", + "main": "libs/shared/sentry-next/src/index.ts", + "tsConfig": "libs/shared/sentry-next/tsconfig.lib.json", + "assets": ["libs/shared/sentry-next/*.md"], + "generatePackageJson": true, + "format": ["cjs"] + } + }, + "test-unit": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/sentry-next/jest.config.ts" + } + } + } +} diff --git a/libs/shared/sentry-next/src/index.ts b/libs/shared/sentry-next/src/index.ts new file mode 100644 index 00000000000..57216b2c1ba --- /dev/null +++ b/libs/shared/sentry-next/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/client'; +export * from './lib/server'; diff --git a/libs/shared/sentry-next/src/lib/client.ts b/libs/shared/sentry-next/src/lib/client.ts new file mode 100644 index 00000000000..9b7dc6588fe --- /dev/null +++ b/libs/shared/sentry-next/src/lib/client.ts @@ -0,0 +1,46 @@ +/* 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 { beforeSendBrowser } from '@fxa/shared/sentry-utils'; +import { + Logger, + buildSentryConfig, + SentryConfigOpts, +} from '@fxa/shared/sentry-utils'; + +export async 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, + beforeSend: function (event: Sentry.ErrorEvent) { + return beforeSendBrowser(opts, event); + }, + }); + } catch (e) { + log.error(e); + } +} diff --git a/libs/shared/sentry-next/src/lib/server.ts b/libs/shared/sentry-next/src/lib/server.ts new file mode 100644 index 00000000000..7214c61639d --- /dev/null +++ b/libs/shared/sentry-next/src/lib/server.ts @@ -0,0 +1,53 @@ +/* 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, EventHint } from '@sentry/types'; +import { + Logger, + SentryConfigOpts, + buildSentryConfig, + beforeSendServer, +} from '@fxa/shared/sentry-utils'; + +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); + const integrations = [ + // Default + Sentry.extraErrorDataIntegration({ depth: 5 }), + + // Custom Integrations + ...(config.integrations || []), + ]; + + try { + Sentry.init({ + // Defaults Options + normalizeDepth: 6, + maxValueLength: 500, + + // Custom Options + integrations, + beforeSend: function (event: ErrorEvent, hint: EventHint) { + return beforeSendServer(config, event, hint); + }, + ...opts, + }); + } catch (e) { + log.debug('init-sentry', { msg: 'Issue initializing sentry!' }); + log.error('init-sentry', e); + } +} diff --git a/libs/shared/sentry-next/tsconfig.json b/libs/shared/sentry-next/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/sentry-next/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/sentry-next/tsconfig.lib.json b/libs/shared/sentry-next/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/sentry-next/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/sentry-next/tsconfig.spec.json b/libs/shared/sentry-next/tsconfig.spec.json new file mode 100644 index 00000000000..69a251f328c --- /dev/null +++ b/libs/shared/sentry-next/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry-node/.eslintrc.json b/libs/shared/sentry-node/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/sentry-node/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/sentry-node/README.md b/libs/shared/sentry-node/README.md new file mode 100644 index 00000000000..7b26fd0be5d --- /dev/null +++ b/libs/shared/sentry-node/README.md @@ -0,0 +1,11 @@ +# shared-sentry-node + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-sentry-node` to build the library. + +## Running unit tests + +Run `nx test shared-sentry-node` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/sentry-node/jest.config.ts b/libs/shared/sentry-node/jest.config.ts new file mode 100644 index 00000000000..986e678259c --- /dev/null +++ b/libs/shared/sentry-node/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-sentry-node', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/sentry-node', +}; diff --git a/libs/shared/sentry-node/package.json b/libs/shared/sentry-node/package.json new file mode 100644 index 00000000000..e171c04c158 --- /dev/null +++ b/libs/shared/sentry-node/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fxa/shared/sentry-node", + "version": "0.0.1", + "dependencies": {}, + "type": "commonjs", + "main": "./index.cjs", + "private": true +} diff --git a/libs/shared/sentry-node/project.json b/libs/shared/sentry-node/project.json new file mode 100644 index 00000000000..fb6b27a62b1 --- /dev/null +++ b/libs/shared/sentry-node/project.json @@ -0,0 +1,28 @@ +{ + "name": "shared-sentry-node", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/sentry-node/src", + "projectType": "library", + "tags": ["scope:shared:lib"], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/sentry-node", + "main": "libs/shared/sentry-node/src/index.ts", + "tsConfig": "libs/shared/sentry-node/tsconfig.lib.json", + "assets": ["libs/shared/sentry-node/*.md"], + "generatePackageJson": true, + "format": ["cjs"] + } + }, + "test-unit": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/sentry-node/jest.config.ts" + } + } + } +} diff --git a/libs/shared/sentry-node/src/index.ts b/libs/shared/sentry-node/src/index.ts new file mode 100644 index 00000000000..595d792b30f --- /dev/null +++ b/libs/shared/sentry-node/src/index.ts @@ -0,0 +1,3 @@ +export * from './lib/joi-message-overrides'; +export * from './lib/node'; +export * from './lib/report-validation-error'; diff --git a/libs/shared/sentry-node/src/lib/joi-message-overrides.spec.ts b/libs/shared/sentry-node/src/lib/joi-message-overrides.spec.ts new file mode 100644 index 00000000000..1d0798027ea --- /dev/null +++ b/libs/shared/sentry-node/src/lib/joi-message-overrides.spec.ts @@ -0,0 +1,25 @@ +/* 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 joi from 'joi'; +import { overrideJoiMessages } from './joi-message-overrides'; + +describe('joi-message-overrides', () => { + it('overrides default message for regex', () => { + const validators = { + test: joi.string().regex(/test/), + }; + const result1 = validators.test.validate('foobar').error?.message; + + const validators2 = overrideJoiMessages(validators); + const result2 = validators2['test'].validate('foobar').error?.message; + + expect(validators2).toBeDefined(); + expect(result1).toBeDefined(); + expect(result2).toBeDefined(); + expect(result1).not.toEqual(result2); + expect(result1).toContain('with value'); + expect(result2).not.toContain('with value'); + }); +}); diff --git a/libs/shared/sentry-node/src/lib/joi-message-overrides.ts b/libs/shared/sentry-node/src/lib/joi-message-overrides.ts new file mode 100644 index 00000000000..08cb4c44588 --- /dev/null +++ b/libs/shared/sentry-node/src/lib/joi-message-overrides.ts @@ -0,0 +1,40 @@ +/* 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 { AnySchema } from 'joi'; + +/** + * A set of default message overrides. These result in better error resolution in sentry. + */ +export const defaultMessageOverrides = { + // Override some of the message defaults. Here we remove the 'with value {:[.]}' + // portion of the message, because it causes too much fragmentation in our sentry + // errors. These should be applied to any .regex or .pattern joi validator. + // Form more context concerning overriding messages see: + // - https://joi.dev/api/?v=17.6.0#anymessagesmessages + // - https://github.com/hapijs/joi/blob/7aa36666863c1dde7e4eb02a8058e00555a99d54/lib/types/string.js#L718 + 'string.pattern.base': + '{{#label}} fails to match the required pattern: {{#regex}}', + 'string.pattern.name': '{{#label}} fails to match the {{#name}} pattern', + 'string.pattern.invert.base': + '{{#label}} matches the inverted pattern: {{#regex}}', + 'string.pattern.invert.name': + '{{#label}} matches the inverted {{#name}} pattern', +}; + +/** + * Applies a set of message overrides to the default joi message formats. + * @param data - Set of joi validators to apply message overrides to to. Note, data is mutated. + * @param overrides - Set of optional overrides, if none are provide the defaultMessageOverrides are used. + * @returns data + */ +export function overrideJoiMessages( + data: Record, + overrides?: Record +) { + Object.keys(data).forEach( + (x) => (data[x] = data[x].messages(overrides || defaultMessageOverrides)) + ); + return data; +} diff --git a/libs/shared/sentry-node/src/lib/node.ts b/libs/shared/sentry-node/src/lib/node.ts new file mode 100644 index 00000000000..abac4a98517 --- /dev/null +++ b/libs/shared/sentry-node/src/lib/node.ts @@ -0,0 +1,50 @@ +/* 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 { ErrorEvent } from '@sentry/types'; +import * as Sentry from '@sentry/node'; +import { + beforeSendServer, + buildSentryConfig, + InitSentryOpts, + Logger, +} from '@fxa/shared/sentry-utils'; + +/** + * There are multiple sentry implementations. For example, @sentry/node and @sentry/nestjs. + * Depending on the application context. + */ +export function initSentry(config: InitSentryOpts, log: Logger) { + if (!config?.sentry?.dsn) { + log.error('init-sentry', 'No Sentry dsn provided. Cannot start sentry'); + return; + } + + const opts = buildSentryConfig(config, log); + + const integrations = [ + Sentry.extraErrorDataIntegration({ depth: 5 }), + + // Custom Integrations + ...(config.integrations || []), + ]; + + try { + Sentry.init({ + // Defaults Options + normalizeDepth: 6, + maxValueLength: 500, + + // Custom Options + integrations, + beforeSend: (event: ErrorEvent, hint: any) => { + return beforeSendServer(config, event, hint); + }, + ...opts, + }); + } catch (e) { + log.debug('init-sentry', { msg: 'Issue initializing sentry!' }); + log.error('init-sentry', e); + } +} diff --git a/libs/shared/sentry-node/src/lib/report-validation-error.ts b/libs/shared/sentry-node/src/lib/report-validation-error.ts new file mode 100644 index 00000000000..10cea11bfb2 --- /dev/null +++ b/libs/shared/sentry-node/src/lib/report-validation-error.ts @@ -0,0 +1,60 @@ +/* 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/node'; +import { ValidationError } from 'joi'; + +/** + * Format a Stripe product/plan metadata validation error message for + * Sentry to include as much detail as possible about what metadata + * failed validation and in what way. + * + * @param {string} planId + * @param {string | ValidationError} error + */ +export function formatMetadataValidationErrorMessage( + planId: string, + error: ValidationError +) { + let msg = `${planId} metadata invalid:`; + if (typeof error === 'string') { + msg = `${msg} ${error}`; + } else { + msg = `${msg}${error.details + .map(({ message }) => ` ${message};`) + .join('')}`; + } + return msg; +} + +/** + * Report a validation error to Sentry with validation details. + * + * @param {Pick} sentry - Current sentry instance. Note, that this subtype is being + * used instead of directly accessing the sentry instance inorder to be context agnostic. + * @param {*} message + * @param {string | ValidationError} ValidationError error + */ +export function reportValidationError( + message: any, + error: ValidationError | string +) { + const details: any = {}; + if (typeof error === 'string') { + details.error = error; + } else { + for (const errorItem of error.details || []) { + const key = errorItem.path.join('.'); + details[key] = { + message: errorItem.message, + type: errorItem.type, + }; + } + } + + Sentry.withScope((scope) => { + scope.setContext('validationError', details); + Sentry.captureMessage(message, 'error'); + }); +} diff --git a/libs/shared/sentry-node/tsconfig.json b/libs/shared/sentry-node/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/sentry-node/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/sentry-node/tsconfig.lib.json b/libs/shared/sentry-node/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/sentry-node/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/sentry-node/tsconfig.spec.json b/libs/shared/sentry-node/tsconfig.spec.json new file mode 100644 index 00000000000..69a251f328c --- /dev/null +++ b/libs/shared/sentry-node/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry-utils/.eslintrc.json b/libs/shared/sentry-utils/.eslintrc.json new file mode 100644 index 00000000000..3456be9b903 --- /dev/null +++ b/libs/shared/sentry-utils/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/shared/sentry-utils/README.md b/libs/shared/sentry-utils/README.md new file mode 100644 index 00000000000..3b09a775391 --- /dev/null +++ b/libs/shared/sentry-utils/README.md @@ -0,0 +1,11 @@ +# shared-sentry-utils + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build shared-sentry-utils` to build the library. + +## Running unit tests + +Run `nx test shared-sentry-utils` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/sentry-utils/jest.config.ts b/libs/shared/sentry-utils/jest.config.ts new file mode 100644 index 00000000000..a62888564b3 --- /dev/null +++ b/libs/shared/sentry-utils/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'shared-sentry-utils', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/shared/sentry-utils', +}; diff --git a/libs/shared/sentry-utils/package.json b/libs/shared/sentry-utils/package.json new file mode 100644 index 00000000000..1a8293c67b0 --- /dev/null +++ b/libs/shared/sentry-utils/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fxa/shared/sentry-utils", + "version": "0.0.1", + "dependencies": {}, + "type": "commonjs", + "main": "./index.cjs", + "private": true +} diff --git a/libs/shared/sentry-utils/project.json b/libs/shared/sentry-utils/project.json new file mode 100644 index 00000000000..06dcb58a9eb --- /dev/null +++ b/libs/shared/sentry-utils/project.json @@ -0,0 +1,27 @@ +{ + "name": "shared-sentry-utils", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/sentry-utils/src", + "projectType": "library", + "tags": ["scope:shared:lib"], + "targets": { + "build": { + "executor": "@nx/esbuild:esbuild", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/shared/sentry-utils", + "main": "libs/shared/sentry-utils/src/index.ts", + "tsConfig": "libs/shared/sentry-utils/tsconfig.lib.json", + "assets": ["libs/shared/sentry-utils/*.md"], + "format": ["cjs"] + } + }, + "test-unit": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "libs/shared/sentry-utils/jest.config.ts" + } + } + } +} diff --git a/libs/shared/sentry-utils/src/index.ts b/libs/shared/sentry-utils/src/index.ts new file mode 100644 index 00000000000..ddff1ad82ef --- /dev/null +++ b/libs/shared/sentry-utils/src/index.ts @@ -0,0 +1,9 @@ +export * from './lib/utils'; +export * from './lib/before-send.server'; +export * from './lib/before-send.browser'; +export * from './lib/sentry.types'; +export * from './lib/config-builder'; +export * from './lib/pii/filters'; +export * from './lib/pii/filter-actions'; +export * from './lib/models/pii'; +export * from './lib/models/sentry-config-opts'; diff --git a/libs/shared/sentry-utils/src/lib/before-send.browser.spec.ts b/libs/shared/sentry-utils/src/lib/before-send.browser.spec.ts new file mode 100644 index 00000000000..39c63add251 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/before-send.browser.spec.ts @@ -0,0 +1,167 @@ +/* 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/sentry-config-opts'; +import { + beforeSendBrowser, + disableSentry, + enableSentry, + isSentryEnabled, +} from './before-send.browser'; + +const config: SentryConfigOpts = { + release: 'v0.0.0', + sentry: { + dsn: 'https://public:private@host:8080/1', + env: 'test', + clientName: 'fxa-shared-testing', + sampleRate: 0, + }, +}; + +describe('disable / enables', () => { + beforeEach(() => { + // Make sure it's enabled by default + enableSentry(); + }); + + it('enables', () => { + enableSentry(); + expect(isSentryEnabled()).toBeTruthy(); + }); + + it('disables', () => { + disableSentry(); + expect(isSentryEnabled()).toBeFalsy(); + }); + + it('will return null from before send when disabled', () => { + disableSentry(); + expect(beforeSendBrowser({}, { type: undefined }, {})).toBeNull(); + }); +}); + +describe('before send', () => { + beforeEach(() => { + // Make sure it's enabled by default + enableSentry(); + }); + + it('works without request url', () => { + const data = { + type: undefined, + key: 'value', + }; + const resultData = beforeSendBrowser(config, data, {}); + + expect(resultData).toEqual(data); + }); + + it('fingerprints errno', () => { + const data = { + type: undefined, + request: { + url: 'https://example.com', + }, + tags: { + errno: '100', + }, + }; + + const resultData = beforeSendBrowser(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 = { + type: undefined, + request: { + url: url + badQuery, + }, + }; + + const goodData = { + request: { + url: url + goodQuery, + }, + }; + + const resultData = beforeSendBrowser(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 = { + type: undefined, + request: { + headers: { + Referer: url + badQuery, + }, + }, + }; + + const goodData = { + request: { + headers: { + Referer: url + goodQuery, + }, + }, + }; + + const resultData = beforeSendBrowser(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 = { + type: undefined, + culprit: badCulprit, + exception: { + values: [ + { + stacktrace: { + frames: [ + { + abs_path: badAbsPath, // eslint-disable-line camelcase + }, + { + abs_path: badAbsPath, // eslint-disable-line camelcase + }, + ], + }, + }, + ], + }, + request: { + url, + }, + }; + + const resultData = beforeSendBrowser(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); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/before-send.browser.ts b/libs/shared/sentry-utils/src/lib/before-send.browser.ts new file mode 100644 index 00000000000..cd9fc6f141c --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/before-send.browser.ts @@ -0,0 +1,74 @@ +import { ErrorEvent, EventHint, Exception } from '@sentry/types'; +import { + SentryConfigOpts, + tagFxaName, + cleanUpQueryParam, +} from '@fxa/shared/sentry-utils'; + +// Internal flag to keep track of whether or not sentry is initialized +let sentryEnabled = false; +export function disableSentry() { + sentryEnabled = false; +} +export function enableSentry() { + sentryEnabled = true; +} +export function isSentryEnabled() { + return sentryEnabled; +} + +/** + * 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 beforeSendBrowser( + opts: SentryConfigOpts, + event: ErrorEvent, + hint?: EventHint +) { + 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: 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; +} diff --git a/libs/shared/sentry-utils/src/lib/before-send.server.spec.ts b/libs/shared/sentry-utils/src/lib/before-send.server.spec.ts new file mode 100644 index 00000000000..98e97a650ef --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/before-send.server.spec.ts @@ -0,0 +1,33 @@ +/* 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 { beforeSendServer } from './before-send.server'; +import { filterSentryEvent } from './utils'; + +describe('beforeSendServer', () => { + const config = { + release: 'v0.0.0', + sentry: { + dsn: 'https://public:private@host:8080/1', + env: 'test', + clientName: 'fxa-shared-testing', + sampleRate: 0, + }, + eventFilters: [filterSentryEvent], + }; + + it('adjust event before send', () => { + const data = { + type: undefined, + key: 'value', + extra: { + uid: '123', + }, + }; + const hint = {}; + const modified = beforeSendServer(config, data, hint); + expect(modified?.tags?.['fxa.name']).toEqual('unknown'); + expect(modified?.extra?.['uid']).toEqual('[Filtered]'); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/before-send.server.ts b/libs/shared/sentry-utils/src/lib/before-send.server.ts new file mode 100644 index 00000000000..6ebcc341bcf --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/before-send.server.ts @@ -0,0 +1,29 @@ +/* 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 { InitSentryOpts } from './models/sentry-config-opts'; +import { tagFxaName } from './utils'; +import { ErrorEvent } from '@sentry/types'; + +export function beforeSendServer( + config: InitSentryOpts, + event: ErrorEvent, + hint: any +) { + if (config.ignoreErrors) { + const ignore = config.ignoreErrors(event); + if (ignore) { + return null; + } + } + + // Default + event = tagFxaName(event, config.sentry?.serverName || 'unknown'); + + // Custom filters + config.eventFilters?.forEach((filter) => { + event = filter(event, hint); + }); + return event; +} diff --git a/libs/shared/sentry-utils/src/lib/config-builder.spec.ts b/libs/shared/sentry-utils/src/lib/config-builder.spec.ts new file mode 100644 index 00000000000..aa8b8833c87 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/config-builder.spec.ts @@ -0,0 +1,133 @@ +/* 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 { buildSentryConfig } from './config-builder'; +import { SentryConfigOpts } from './models/sentry-config-opts'; +import { Logger } from './sentry.types'; + +describe('config-builder', () => { + function cloneConfig(val: any) { + return structuredClone(val); + } + + const mockLogger: Logger = { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + + const testConfig: SentryConfigOpts = { + release: '1.0.1', + version: '1.0.2', + sentry: { + dsn: 'https://foo.sentry.io', + env: 'test', + sampleRate: 1, + serverName: 'fxa-shared-test', + clientName: 'fxa-shared-client-test', + }, + }; + + it('builds', () => { + const config = buildSentryConfig(testConfig, mockLogger); + expect(config).toBeDefined(); + expect(mockLogger.info).toBeCalledWith('sentry-config-builder', { + msg: `Config setting for sentry.dsn specified, enabling sentry for env ${testConfig.sentry?.env}!`, + }); + }); + + it('picks correct defaults', () => { + const config = buildSentryConfig(testConfig, mockLogger); + expect(config.environment).toEqual(testConfig.sentry?.env); + expect(config.release).toEqual(testConfig.release); + expect(config.fxaName).toEqual(testConfig.sentry?.clientName); + }); + + it('falls back', () => { + const clone = cloneConfig(testConfig); + delete clone.sentry.clientName; + delete clone.release; + + const config = buildSentryConfig(clone, mockLogger); + + expect(config.release).toEqual(testConfig.version); + expect(config.fxaName).toEqual(testConfig.sentry?.serverName); + }); + + it('warns about missing config', () => { + const clone = cloneConfig(testConfig); + clone.sentry.dsn = ''; + + buildSentryConfig(clone, mockLogger); + + expect(mockLogger.warn).toBeCalledTimes(1); + }); + + it('errors on missing dsn', () => { + const clone = cloneConfig(testConfig); + clone.sentry.strict = true; + clone.sentry.dsn = ''; + + expect(() => { + buildSentryConfig(clone, mockLogger); + }).toThrow('sentry.dsn not specified. sentry disabled.'); + expect(mockLogger.warn).toBeCalledTimes(1); + }); + + it('errors on unknown environment', () => { + const clone = cloneConfig(testConfig); + clone.sentry.strict = true; + clone.sentry.env = 'xyz'; + + expect(() => { + buildSentryConfig(clone, mockLogger); + }).toThrow( + 'invalid config.env. xyz options are: test,local,dev,ci,stage,prod,production,development' + ); + expect(mockLogger.warn).toBeCalledTimes(1); + }); + + it('errors on missing release', () => { + const clone = cloneConfig(testConfig); + clone.sentry.strict = true; + delete clone.release; + delete clone.version; + + expect(() => { + buildSentryConfig(clone, mockLogger); + }).toThrow('config missing either release or version.'); + expect(mockLogger.warn).toBeCalledTimes(1); + }); + + it('errors on missing sampleRate', () => { + const clone = cloneConfig(testConfig); + clone.sentry.strict = true; + delete clone.sentry.sampleRate; + + expect(() => { + buildSentryConfig(clone, mockLogger); + }).toThrow('sentry.sampleRate'); + expect(mockLogger.warn).toBeCalledTimes(1); + }); + + it('can use moz logger', () => { + const mozlog = require('mozlog')({ + app: 'fxa-shared-test', + level: 'trace', + }); + const logger = mozlog('fxa-shared-testing'); + const config = buildSentryConfig(testConfig, logger); + + expect(config).toBeDefined(); + }); + + it('can use console logger', () => { + const config = buildSentryConfig(testConfig, console); + expect(config).toBeDefined(); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/config-builder.ts b/libs/shared/sentry-utils/src/lib/config-builder.ts new file mode 100644 index 00000000000..9da5e2b4f93 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/config-builder.ts @@ -0,0 +1,87 @@ +/* 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 { Logger } from './sentry.types'; +import { SentryConfigOpts } from './models/sentry-config-opts'; + +const sentryEnvMap: Record = { + test: 'test', + local: 'local', + dev: 'dev', + ci: 'ci', + stage: 'stage', + prod: 'prod', + production: 'prod', + development: 'dev', +}; + +function toEnv(val: any) { + if (typeof val === 'string') { + return sentryEnvMap[val] || ''; + } + return ''; +} + +export function buildSentryConfig(config: SentryConfigOpts, log: Logger) { + if (log) { + checkSentryConfig(config, log); + } + + const opts = { + dsn: config.sentry?.dsn || '', + release: config.release || config.version, + environment: toEnv(config.sentry?.env), + sampleRate: config.sentry?.sampleRate, + clientName: config.sentry?.clientName, + serverName: config.sentry?.serverName, + fxaName: config.sentry?.clientName || config.sentry?.serverName, + tracesSampleRate: config.sentry?.tracesSampleRate, + }; + + return opts; +} + +function checkSentryConfig(config: SentryConfigOpts, log: Logger) { + if (!config || !config.sentry || !config.sentry?.dsn) { + raiseError('sentry.dsn not specified. sentry disabled.'); + return; + } else { + log?.info('sentry-config-builder', { + msg: `Config setting for sentry.dsn specified, enabling sentry for env ${config.sentry.env}!`, + }); + } + + if (!config.sentry.env) { + raiseError('config missing either environment or env.'); + } else if (!toEnv(config.sentry.env)) { + raiseError( + `invalid config.env. ${config.sentry.env} options are: ${Object.keys( + sentryEnvMap + ).join(',')}` + ); + } else { + log?.info('sentry-config-builder', { + msg: 'sentry targeting: ' + sentryEnvMap[config.sentry.env], + }); + } + + if (!config.release && !config.version) { + raiseError('config missing either release or version.'); + } + + if (config.sentry?.sampleRate == null) { + raiseError('config missing sentry.sampleRate'); + } + if (!config.sentry.clientName && !config.sentry.serverName) { + raiseError('config missing either sentry.clientName or sentry.serverName'); + } + + function raiseError(msg: string) { + log?.warn('sentry-config-builder', { msg }); + if (config.sentry?.strict) { + throw new SentryConfigurationBuildError(msg); + } + } +} + +class SentryConfigurationBuildError extends Error {} diff --git a/libs/shared/sentry-utils/src/lib/models/pii.ts b/libs/shared/sentry-utils/src/lib/models/pii.ts new file mode 100644 index 00000000000..349cbbcc7b9 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/models/pii.ts @@ -0,0 +1,41 @@ +/* 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/. */ + +/** A general type that holds PII data. */ +export type PiiData = Record | string | undefined | null; + +/** + * Result of a filter action. + */ +export interface FilterActionResult { + /** + * The modified value + */ + val: T; + + /** + * Whether or not the pipeline can be exited. In the event the filter removes enough data, it might + * make sense to exit the pipeline of filter actions early. + */ + exitPipeline: boolean; +} + +/** A general interface for running a filter action on PII Data */ +export interface IFilterAction { + /** + * Filters a value for PII + * @param val - the value to filter + * @param depth - if filtering an object, the depth of the current traversal + * @returns the provided value with modifications, and flag if the action pipeline can be exited. + */ + execute(val: T, depth?: number): FilterActionResult; +} + +/** A general interface for top level classes that filter PII data */ +export interface IFilter { + filter(event: PiiData): PiiData; +} + +/** Things to check for when scrubbing for PII. */ +export type CheckOnly = 'keys' | 'values' | 'both'; diff --git a/libs/shared/sentry-utils/src/lib/models/sentry-config-opts.ts b/libs/shared/sentry-utils/src/lib/models/sentry-config-opts.ts new file mode 100644 index 00000000000..b8728fe5b0f --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/models/sentry-config-opts.ts @@ -0,0 +1,41 @@ +/* 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 { ErrorEvent } from '@sentry/types'; + +export type SentryConfigOpts = { + /** Name of release */ + release?: string; + + /** Fall back for name of release */ + version?: string; + + /** Sentry specific settings */ + sentry?: { + /** The datasource name. This value can be obtained from the sentry portal. */ + dsn?: string; + /** The environment name. */ + env?: string; + /** The rate (as percent between 0 and 1) at which errors are sampled. Can be reduced to decrease data volume. */ + sampleRate?: number; + /** The name of the active client. */ + clientName?: string; + /** The name of the active server. */ + serverName?: string; + + /** When set to true, building a configuration will throw an error critical fields are missing. */ + strict?: boolean; + + /** The tracing sample rate. Setting this above 0 will aso result in performance metrics being captured. */ + tracesSampleRate?: number; + }; +}; + +export type ExtraOpts = { + ignoreErrors?: (error: any) => boolean | undefined; + integrations?: any[]; + eventFilters?: Array<(event: ErrorEvent, hint: any) => ErrorEvent>; +}; + +export type InitSentryOpts = SentryConfigOpts & ExtraOpts; diff --git a/libs/shared/sentry-utils/src/lib/pii/filter-actions.spec.ts b/libs/shared/sentry-utils/src/lib/pii/filter-actions.spec.ts new file mode 100644 index 00000000000..1adb42f4101 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/pii/filter-actions.spec.ts @@ -0,0 +1,380 @@ +/* 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 uuid from 'uuid'; + +import { + CommonPiiActions, + DepthFilter, + TRUNCATED, + FILTERED, + PiiRegexFilter, + BreadthFilter, +} from './filter-actions'; + +describe('pii-filter-actions', () => { + describe('DepthFilter', () => { + it('truncates objects', () => { + const filter = new DepthFilter(1); + + expect(filter.execute('foo', 1)).toEqual({ + val: 'foo', + exitPipeline: false, + }); + expect(filter.execute(null, 1)).toEqual({ + val: null, + exitPipeline: true, + }); + }); + + it('truncates objects when depth is greater than max depth', () => { + const filter = new DepthFilter(1); + expect(filter.execute({ foo: 'bar' }, 2)).toEqual({ + val: { + foo: TRUNCATED, + }, + exitPipeline: true, + }); + }); + + it('does not truncate if depth is less than max depth ', () => { + const filter = new DepthFilter(1); + expect(filter.execute({ foo: 'bar' }, 0)).toEqual({ + val: { foo: 'bar' }, + exitPipeline: false, + }); + }); + + it('handles null', () => { + const filter = new DepthFilter(1); + expect(filter.execute(null, 1)).toEqual({ + val: null, + exitPipeline: true, + }); + }); + }); + + describe('BreadthFilter', () => { + it('truncates objects', () => { + const filter = new BreadthFilter(1); + + expect(filter.execute('foo')).toEqual({ + val: 'foo', + exitPipeline: false, + }); + expect(filter.execute(null)).toEqual({ + val: null, + exitPipeline: true, + }); + }); + + it('truncates object of size greater than max breadth', () => { + const filter = new BreadthFilter(1); + expect(filter.execute({ foo: '1', bar: '2', baz: '3' })).toEqual({ + val: { + foo: '1', + [TRUNCATED]: 2, + }, + exitPipeline: false, + }); + }); + + it('does not truncate object of size equal to max breadth', () => { + const filter = new BreadthFilter(3); + expect(filter.execute(['foo', 'bar', 'baz'])).toEqual({ + val: ['foo', 'bar', 'baz'], + exitPipeline: false, + }); + }); + + it('does not truncate object of size less than max breadth', () => { + const filter = new BreadthFilter(5); + expect(filter.execute(['foo', 'bar', 'baz'])).toEqual({ + val: ['foo', 'bar', 'baz'], + exitPipeline: false, + }); + }); + + it('truncates array of size greater than max breadth', () => { + const filter = new BreadthFilter(1); + expect(filter.execute(['foo', 'bar', 'baz'])).toEqual({ + val: ['foo', `${TRUNCATED}:2`], + exitPipeline: false, + }); + }); + + it('does not truncate array of size less than max breadth', () => { + const filter = new BreadthFilter(5); + expect(filter.execute(['foo', 'bar', 'baz'])).toEqual({ + val: ['foo', 'bar', 'baz'], + exitPipeline: false, + }); + }); + + it('does not truncate array of size equal to max breadth', () => { + const filter = new BreadthFilter(3); + expect(filter.execute(['foo', 'bar', 'baz'])).toEqual({ + val: ['foo', 'bar', 'baz'], + exitPipeline: false, + }); + }); + + it('handles empty array', () => { + const filter = new BreadthFilter(1); + expect(filter.execute([])).toEqual({ + val: [], + exitPipeline: true, + }); + }); + + it('handles empty object', () => { + const filter = new BreadthFilter(1); + expect(filter.execute({})).toEqual({ + val: {}, + exitPipeline: true, + }); + }); + }); + + describe('PiiRegexFilter', () => { + it('filters string', () => { + const filter = new PiiRegexFilter(/foo/gi, 'values', '[BAR]'); + const value = filter.execute('test foo regex filter'); + expect(value).toEqual({ + val: 'test [BAR] regex filter', + exitPipeline: false, + }); + }); + + it('filters string and determines exitPipeline', () => { + const filter = new PiiRegexFilter(/foo/gi, 'values', '[BAR]'); + const value = filter.execute('foo'); + expect(value).toEqual({ val: '[BAR]', exitPipeline: true }); + }); + + it('filters object value', () => { + const filter1 = new PiiRegexFilter(/foo/gi, 'both', '[BAR]'); + const filter2 = new PiiRegexFilter(/foo/gi, 'values', '[BAR]'); + + const { val: value1 } = filter1.execute({ + item: 'test foo regex filter', + }); + const { val: value2 } = filter2.execute({ + item: 'test foo regex filter', + }); + + expect(value1.item).toEqual('test [BAR] regex filter'); + expect(value2.item).toEqual('test [BAR] regex filter'); + }); + + it('filters object key', () => { + const filter = new PiiRegexFilter(/foo/gi, 'keys', '[BAR]'); + + const { val: value } = filter.execute({ + foo: 'test foo regex filter', + }); + + expect(value.foo).toEqual('[BAR]'); + }); + + describe('checksOn', () => { + it('checks on values', () => { + const filter = new PiiRegexFilter(/foo/gi, 'values', '[BAR]'); + const { val: value } = filter.execute({ + foo: 'test foo regex filter', + bar: 'test foo regex filter', + }); + expect(value.foo).toEqual('test [BAR] regex filter'); + expect(value.bar).toEqual('test [BAR] regex filter'); + }); + + it('checks on keys', () => { + const filter = new PiiRegexFilter(/foo/gi, 'keys', '[BAR]'); + const { val: value } = filter.execute({ + foo: 'test foo regex filter', + bar: 'test foo regex filter', + }); + expect(value.foo).toEqual('[BAR]'); + expect(value.bar).toEqual('test foo regex filter'); + }); + + it('checks on keys and values', () => { + const filter = new PiiRegexFilter(/foo/gi, 'both', '[BAR]'); + const { val: value } = filter.execute({ + foo: 'test foo regex filter', + bar: 'test foo regex filter', + }); + expect(value.foo).toEqual('[BAR]'); + expect(value.bar).toEqual('test [BAR] regex filter'); + }); + }); + }); + + describe('CommonPiiActions', () => { + it('filters emails', () => { + const { val: result } = CommonPiiActions.emailValues.execute({ + foo: 'email: test@123.com -- 123@test.com --', + bar: '123', + }); + + expect(result).toEqual({ + foo: `email: ${FILTERED} -- ${FILTERED} --`, + bar: '123', + }); + }); + + it('filters email in url', () => { + const { val: result } = CommonPiiActions.emailValues.execute( + 'http://foo.bar/?email=foxkey@mozilla.com&key=1' + ); + expect(result).toEqual(`http://foo.bar/?email=${FILTERED}&key=1`); + }); + + it('filters email in route', () => { + const { val: result } = CommonPiiActions.emailValues.execute( + '/account?email=foxkey@mozilla.com&key=1' + ); + expect(result).toEqual(`/account?email=${FILTERED}&key=1`); + }); + + it('filters email in query', () => { + const { val: result } = CommonPiiActions.emailValues.execute( + `where email='test@mozilla.com'` + ); + + expect(result).toEqual(`where email='${FILTERED}'`); + }); + + it('filters username / password from url', () => { + const { val: result } = CommonPiiActions.urlUsernamePassword.execute( + 'http://me:wut@foo.bar/' + ); + expect(result).toEqual(`http://${FILTERED}:${FILTERED}@foo.bar/`); + }); + + it('ipv6 values', () => { + const { val: result } = CommonPiiActions.ipV6Values.execute({ + foo: 'ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 -- FE80:0000:0000:0000:0202:B3FF:FE1E:8329 --', + bar: '123', + }); + expect(result).toEqual({ + foo: `ipv6: ${FILTERED} -- ${FILTERED} --`, + bar: '123', + }); + }); + + it('ipv4 values', () => { + const { val: result } = CommonPiiActions.ipV4Values.execute({ + foo: '-- 127.0.0.1 -- 10.0.0.1 -- ', + bar: '1.2.3', + }); + expect(result).toEqual({ + foo: `-- ${FILTERED} -- ${FILTERED} -- `, + bar: '1.2.3', + }); + }); + + it('filters pii keys', () => { + const { val: result } = CommonPiiActions.piiKeys.execute({ + 'oidc-test': 'foo', + 'OIDC-TEST': 'foo', + 'remote-groups': 'foo', + 'REMOTE-GROUPS': 'foo', + email_address: 'foo', + email: 'foo', + EmailAddress: 'foo', + ip: 'foo', + ip_addr: 'foo', + ip_address: 'foo', + IpAddress: 'foo', + uid: 'foo', + user: 'foo', + username: 'foo', + user_name: 'foo', + UserName: 'foo', + userid: 'foo', + UserId: 'foo', + user_id: 'foo', + bar: '123', + }); + + expect(result).toEqual({ + 'oidc-test': FILTERED, + 'OIDC-TEST': FILTERED, + 'remote-groups': FILTERED, + 'REMOTE-GROUPS': FILTERED, + email: FILTERED, + email_address: FILTERED, + EmailAddress: FILTERED, + ip: FILTERED, + ip_addr: FILTERED, + ip_address: FILTERED, + IpAddress: FILTERED, + uid: FILTERED, + user: FILTERED, + username: FILTERED, + user_name: FILTERED, + UserName: FILTERED, + userid: FILTERED, + user_id: FILTERED, + UserId: FILTERED, + bar: '123', + }); + }); + + it('filters token values', () => { + const token1 = uuid.v4().replace(/-/g, ''); + const token2 = uuid.v4().replace(/-/g, ''); + const token3 = uuid.v4().toString(); + const { val: result } = CommonPiiActions.tokenValues.execute({ + foo: `-- ${token1}\n${token2}--`, + bar: token3, + }); + + expect(result).toEqual({ + foo: `-- ${FILTERED}\n${FILTERED}--`, + bar: token3, + }); + }); + + it('filters 64 byte token values', () => { + const token1 = uuid.v4().replace(/-/g, ''); + const { val: result } = CommonPiiActions.tokenValues.execute({ + foo: `X'${token1}${token1}'`, + }); + + expect(result).toEqual({ + foo: `X'${FILTERED}'`, + }); + }); + + it('filters token value in url', () => { + const result = CommonPiiActions.tokenValues.execute( + 'https://foo.bar/?uid=12345678123456781234567812345678' + ); + expect(result.val).toEqual(`https://foo.bar/?uid=${FILTERED}`); + }); + + it('filters token value in db statement', () => { + const result = CommonPiiActions.tokenValues.execute( + `Call accountDevices_17(X'cce22e4006d243c895c7596e2cad53d8',500)` + ); + expect(result.val).toEqual(`Call accountDevices_17(X'${FILTERED}',500)`); + }); + + it('filters token value in db query', () => { + const result = CommonPiiActions.tokenValues.execute( + ` where uid = X'cce22e4006d243c895c7596e2cad53d8' ` + ); + expect(result.val).toEqual(` where uid = X'${FILTERED}' `); + }); + + it('filters multiple multiline token values', () => { + const token = '12345678123456781234567812345678'; + const { val: result } = CommonPiiActions.tokenValues.execute( + `${token}--${token}\n${token}` + ); + expect(result).toEqual(`${FILTERED}--${FILTERED}\n${FILTERED}`); + }); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/pii/filter-actions.ts b/libs/shared/sentry-utils/src/lib/pii/filter-actions.ts new file mode 100644 index 00000000000..f07f41901d5 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/pii/filter-actions.ts @@ -0,0 +1,338 @@ +/* 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 { CheckOnly, IFilterAction, PiiData } from '../models/pii'; + +/** Default replacement values */ +export const FILTERED = '[Filtered]'; +export const TRUNCATED = '[Truncated]'; + +/** + * A filter that truncates anything over maxDepth. This is a good first action. + */ +export class DepthFilter implements IFilterAction { + /** + * Crete new depth filter. + * @param maxDepth - The max depth allowed in the tree. The first level is considered is index as 0. + */ + constructor(protected readonly maxDepth = 3) {} + + execute(val: T, depth = 0) { + let exitPipeline = false; + + if (val == null) { + exitPipeline = true; + } else if (depth > this.maxDepth && typeof val === 'object') { + Object.keys(val)?.forEach((x) => { + val[x] = TRUNCATED; + }); + exitPipeline = true; + } + + return { val, exitPipeline }; + } +} + +/** + * A filter truncates any object or array containing too many entries. + */ +export class BreadthFilter implements IFilterAction { + /** + * Create new breadth filter + * @param maxBreadth max number of values in object / array + */ + constructor(protected readonly maxBreadth: number) {} + + execute(val: T) { + let exitPipeline = false; + + if (val == null) { + exitPipeline = true; + } else if (typeof val === 'object') { + if (val instanceof Array) { + exitPipeline = this.maxBreadth == 0 || val.length === 0; + const deleted = val.splice(this.maxBreadth); + + // Leave some indication of what was deleted + if (deleted?.length) { + val.push(`${TRUNCATED}:${deleted.length}`); + } + } else { + const keys = Object.keys(val); + let count = 0; + for (const x of keys) { + if (++count > this.maxBreadth) { + delete val[x]; + } + } + + // Leave some indication of what was deleted + if (count > this.maxBreadth) { + val[TRUNCATED] = count - this.maxBreadth; + } + + exitPipeline = keys.length === 0 || this.maxBreadth === 0; + } + } + return { val, exitPipeline }; + } +} + +/** + * A base class for other PiiFilters. Supports checking keys and values + */ +export abstract class PiiFilter implements IFilterAction { + /** Flag determining if object values should be checked. */ + protected get checkValues() { + return this.checkOnly === 'values' || this.checkOnly === 'both'; + } + + /** Flag determining if object keys should be checked. */ + protected get checkKeys() { + return this.checkOnly === 'keys' || this.checkOnly === 'both'; + } + + /** + * Creates a new regex filter action + * @param checkOnly - Optional directive indicating what to check, a value, an object key, or both. + * @param replaceWith - Optional value indicating what to replace a matched value with. + */ + constructor( + public readonly checkOnly: CheckOnly = 'values', + public readonly replaceWith = FILTERED + ) {} + + /** + * Runs the filter + * @param val - value to filter on. + * @returns a filtered value + */ + public execute(val: T) { + let exitPipeline = false; + + if (val == null) { + exitPipeline = true; + } else if (typeof val === 'string') { + val = this.replaceValues(val) as T; + exitPipeline = val === this.replaceWith; + } else if (typeof val === 'object') { + exitPipeline = true; + + // Mutate object + for (const key of Object.keys(val)) { + if (this.filterKey(key)) { + val[key] = this.replaceWith; + } else if (this.filterValue(val[key])) { + val[key] = this.replaceValues(val[key]); + } + + // Encountering a non truncated or non filtered value means the pipeline must keep running. + if (exitPipeline && val[key] !== this.replaceWith) { + exitPipeline = false; + } + } + } + + return { val, exitPipeline }; + } + + /** + * Indicates if value should be filtered + * @param val + * @returns + */ + protected filterValue(val: any) { + return this.checkValues && typeof val === 'string'; + } + + /** + * Let the sub classes determine how to replace values. + * @param val + */ + protected abstract replaceValues(val: string): string; + + /** + * Let subclasses determine when an object's key should be filtered out. + * @param key + */ + protected abstract filterKey(key: string): boolean; +} + +/** + * Uses a regular expression to scrub PII + */ +export class PiiRegexFilter extends PiiFilter implements IFilterAction { + /** + * Creates a new regex filter action + * @param regex - regular expression to use for filter + * @param checkOnly - Optional directive indicating what to check, a value, an object key, or both. + * @param replaceWith - Optional value indicating what to replace a matched value with. + */ + constructor( + public readonly regex: RegExp, + public override readonly checkOnly: CheckOnly = 'values', + public override readonly replaceWith = FILTERED + ) { + super(checkOnly, replaceWith); + } + + protected override replaceValues(val: string): string { + return val.replace(this.regex, this.replaceWith); + } + + protected override filterKey(key: string): boolean { + const result = this.checkKeys && this.regex.test(key); + + // Tricky edge case. The regex maybe sticky. If so, we need to reset its lastIndex so it does not + // affect a subsequent operation. + if (this.regex.sticky) { + this.regex.lastIndex = 0; + } + return result; + } +} + +/** + * Makes sure that if value is a URL it doesn't have identifying info like the username or password portion of the url. + */ +export class UrlUsernamePasswordFilter extends PiiFilter { + constructor(replaceWith = FILTERED) { + super('values', replaceWith); + } + + protected override replaceValues(val: string) { + const url = tryParseUrl(val); + if (url) { + if (url.username) { + url.username = this.replaceWith; + } + if (url.password) { + url.password = this.replaceWith; + } + val = decodeURI(url.toString()); + } + return val; + } + + protected override filterKey(): boolean { + return false; + } +} + +/** + * Strips emails from data. + */ +export class EmailFilter extends PiiRegexFilter { + private readonly encode = [`'`, `"`, `=`]; + private readonly decode = [`[[[']]]`, `[[["]]]`, `[[[=]]]`]; + + constructor(checkOnly: CheckOnly = 'values', replaceWith = FILTERED) { + super( + // RFC 5322 generalized email regex, ~ 99.99% accurate. + /(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/gim, + checkOnly, + replaceWith + ); + } + + protected override replaceValues(val: string) { + const url = tryParseUrl(val); + if (url) { + if (url.searchParams) { + for (const [key, value] of url.searchParams) { + url.searchParams.set( + key, + value.replace(this.regex, this.replaceWith) + ); + } + } + if (url.pathname) { + url.pathname = url.pathname.replace(this.regex, this.replaceWith); + } + try { + val = decodeURI(url.toString()); + } catch { + // Fallback incase the replaces made the url invalid + val = url.toString(); + } + } + + // Encode/decode to work around weird cases like email='foo@bar.com' which is + // technically a valid email, but ill advised and unlikely. Even if a user had + // this odd example email, the majority of the email would stripped, for example, + // email='[Filtered]' thereby eliminating PII. + this.encode.forEach((x, i) => { + val = val.replace(x, this.decode[i]); + }); + val = val.replace(this.regex, this.replaceWith); + this.decode.forEach((x, i) => { + val = val.replace(x, this.encode[i]); + }); + return val; + } + + protected override filterKey(key: string): boolean { + return false; + } +} + +/** Auxillary method for safely parsing a url. If it can't be parsed returns null. */ +function tryParseUrl(val: string) { + try { + return new URL(val); + } catch (_) { + return null; + } +} + +/** + * Some common PII scrubbing actions + */ +export const CommonPiiActions = { + /** + * Limits object/arrays no more than 50 values. + */ + breadthFilter: new BreadthFilter(50), + + /** + * Limits objects to 5 levels of depth + */ + depthFilter: new DepthFilter(5), + + /** + * Makes sure the user name / password is stripped out of the url. + */ + urlUsernamePassword: new UrlUsernamePasswordFilter(), + + /** + * Makes sure emails are stripped from data. Uses RFC 5322 generalized email regex, ~ 99.99% accurate. + */ + emailValues: new EmailFilter(), + + /** + * Matches IP V6 values + */ + ipV6Values: new PiiRegexFilter( + /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gim + ), + + /** + * Matches IPV4 values + */ + ipV4Values: new PiiRegexFilter( + /(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/gim + ), + + /** + * Looks for keys that commonly contain PII + */ + piiKeys: new PiiRegexFilter( + /^oidc-.*|^remote-groups$|^uid$|^email_?|^ip_?|^user$|^user_?(id|name)$/i, + 'keys' + ), + + /** + * Matches uid, session, oauth and other common tokens which we would prefer not to include in Sentry reports. + */ + tokenValues: new PiiRegexFilter(/[a-fA-F0-9]{32,}|[a-fA-F0-9]{64,}/gim), +}; diff --git a/libs/shared/sentry-utils/src/lib/pii/filters.spec.ts b/libs/shared/sentry-utils/src/lib/pii/filters.spec.ts new file mode 100644 index 00000000000..a39d0da7d2e --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/pii/filters.spec.ts @@ -0,0 +1,355 @@ +/* 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 { ErrorEvent } from '@sentry/types'; +import { SQS } from 'aws-sdk'; +import { IFilterAction, PiiData } from '../models/pii'; +import { + CommonPiiActions, + FILTERED, + PiiRegexFilter, + TRUNCATED, +} from './filter-actions'; +import { FilterBase, SentryPiiFilter, SqsMessageFilter } from './filters'; +import { Logger } from '../sentry.types'; + +describe('pii-filters', () => { + describe('SentryMessageFilter', () => { + const sentryFilter = new SentryPiiFilter([ + CommonPiiActions.breadthFilter, + CommonPiiActions.depthFilter, + CommonPiiActions.urlUsernamePassword, + CommonPiiActions.emailValues, + CommonPiiActions.piiKeys, + CommonPiiActions.tokenValues, + CommonPiiActions.ipV4Values, + CommonPiiActions.ipV6Values, + new PiiRegexFilter(/foo/gi), + ]); + + it('filters empty event', () => { + let event: ErrorEvent = { type: undefined }; + event = sentryFilter.filter(event); + expect(event).toEqual({ type: undefined }); + }); + + it('filters event', () => { + let event: ErrorEvent = { + message: 'A foo message.', + contexts: { + ValidationError: { + _original: { + email: `foo@bar.com`, + }, + details: [ + { + context: { + key: 'email', + label: 'email', + name: '[undefined]', + regex: {}, + value: 'none', + }, + message: `foo@bar.com fails to match email pattern`, + path: ['email'], + type: 'string.pattern.base', + }, + ], + type: 'ValidationError', + }, + }, + breadcrumbs: [ + { + message: 'A foo breadcrumb', + data: { + first_name: 'foo', + last_name: 'bar', + }, + }, + { + message: 'A fine message', + }, + ], + request: { + url: 'http://me:123@foo.bar/?email=foxkey@mozilla.com&uid=12345678123456781234567812345678', + query_string: { + email: 'foo', + uid: 'bar', + }, + cookies: { + user: 'foo:bar', + }, + env: { + key: '--foo', + }, + headers: { + foo: 'a foo header', + bar: 'a foo bar bar header', + 'oidc-claim': 'claim1', + }, + data: { + info: { + email: 'foxkeh@mozilla.com', + uid: '12345678123456781234567812345678', + }, + time: new Date(0).getTime(), + }, + }, + exception: { + values: [ + { + value: + 'Foo bar! A user with email foxkeh@mozilla.clom and ip 127.0.0.1 encountered an err.', + }, + ], + }, + extra: { + meta: { + email: 'foo@bar.com', + }, + foo: Array(51).fill('bar'), + l1: { + l2: { + l3: { + l4: { + l5: { + l6: { + l7: { + l8: { + l9: { + l10: 'bar', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + user: { + meta: { + email: 'foo@bar.com', + }, + id: 'foo123', + ip_address: '127.0.0.1', + email: 'foo@bar.com', + username: 'foo.bar', + }, + type: undefined, + spans: undefined, // Not testing, let's be careful not put PII in spans, + measurements: undefined, // NA, just numbers + debug_meta: undefined, // NA, image data + sdkProcessingMetadata: undefined, // NA, not used + }; + + event = sentryFilter.filter(event); + + expect(event).toEqual({ + message: `A ${FILTERED} message.`, + contexts: { + ValidationError: { + _original: { + email: '[Filtered]', + }, + details: [ + { + context: { + key: 'email', + label: 'email', + name: '[undefined]', + regex: {}, + value: 'none', + }, + message: '[Filtered] fails to match email pattern', + path: ['email'], + type: 'string.pattern.base', + }, + ], + type: 'ValidationError', + }, + }, + breadcrumbs: [ + { + message: `A ${FILTERED} breadcrumb`, + data: { + first_name: FILTERED, + last_name: 'bar', + }, + }, + { + message: 'A fine message', + }, + ], + request: { + url: `http://${FILTERED}:${FILTERED}@${FILTERED}.bar/?email=${FILTERED}&uid=${FILTERED}`, + query_string: { + email: FILTERED, + uid: FILTERED, + }, + cookies: { + user: FILTERED, + }, + env: { + key: `--${FILTERED}`, + }, + headers: { + foo: `a ${FILTERED} header`, + bar: `a ${FILTERED} bar bar header`, + 'oidc-claim': `${FILTERED}`, + }, + data: { + info: { + email: `${FILTERED}`, + uid: `${FILTERED}`, + }, + time: new Date(0).getTime(), + }, + }, + exception: { + values: [ + { + value: `${FILTERED} bar! A user with email ${FILTERED} and ip ${FILTERED} encountered an err.`, + }, + ], + }, + extra: { + meta: { + email: FILTERED, + }, + foo: [...Array(50).fill('bar'), `${TRUNCATED}:1`], + l1: { + l2: { + l3: { + l4: { + l5: { + l6: TRUNCATED, + }, + }, + }, + }, + }, + }, + user: { + meta: { + email: FILTERED, + }, + id: `${FILTERED}123`, + ip_address: FILTERED, + email: FILTERED, + username: FILTERED, + }, + type: undefined, + spans: undefined, // Not testing, let's be careful not put PII in spans, + measurements: undefined, // NA, just numbers + debug_meta: undefined, // NA, image data + sdkProcessingMetadata: undefined, // NA, not used + }); + }); + }); + + describe('SqsMessageFilter', () => { + const sqsFilter = new SqsMessageFilter([new PiiRegexFilter(/foo/gi)]); + + it('filters body', () => { + let msg = { Body: 'A message with foo in it.' } as SQS.Message; + msg = sqsFilter.filter(msg); + + expect(msg).toEqual({ + Body: `A message with ${FILTERED} in it.`, + }); + }); + }); + + describe('Deals with Bad Filter', () => { + const mockLogger: Logger = { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + + afterEach(() => { + jest.restoreAllMocks(); + }); + + class BadAction implements IFilterAction { + execute( + val: T, + depth?: number + ): { val: T; exitPipeline: boolean } { + throw new Error('Boom'); + } + } + + class BadFilter extends FilterBase { + constructor(logger: Logger) { + super([new BadAction()], logger); + } + + filter(data: any): any { + return this.applyFilters(data); + } + } + + it('handles errors and logs them', () => { + const badFilter = new BadFilter(mockLogger); + badFilter.filter({ foo: 'bar' }); + expect(mockLogger.error).toBeCalled(); + }); + }); + + describe('Short Circuits', () => { + class ShortCircuit implements IFilterAction { + execute(val: T, depth?: number) { + if (typeof val === 'string') { + val = FILTERED as T; + } else if (typeof val === 'object') { + for (const key in val) { + val[key] = FILTERED; + } + } + return { val, exitPipeline: true }; + } + } + + const shortCircuit = new ShortCircuit(); + const noAction = { + execute: jest.fn(), + }; + + const sentryFilter = new SentryPiiFilter([ + shortCircuit, + noAction as IFilterAction, + ]); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('shorts circuits', () => { + // The fact this runs with out error, indicates badAction was never invoked + const event = sentryFilter.filter({ + type: undefined, + request: { + url: 'http://foo.bar', + query_string: { + foo: 'bar', + }, + headers: { + foo: 'bar', + }, + data: { + info: { + foo: 'bar', + }, + time: new Date(0).getTime(), + }, + }, + }); + expect(noAction.execute).toHaveBeenCalledTimes(0); + expect(event).toBeDefined(); + }); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/pii/filters.ts b/libs/shared/sentry-utils/src/lib/pii/filters.ts new file mode 100644 index 00000000000..3e861596949 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/pii/filters.ts @@ -0,0 +1,193 @@ +/* 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 { ErrorEvent, Event } from '@sentry/types'; + +import { SQS } from 'aws-sdk'; + +import { IFilterAction, PiiData } from '../models/pii'; +import { Logger } from '../sentry.types'; + +/** + * Base class for all filters + */ +export abstract class FilterBase { + constructor( + protected readonly actions: IFilterAction[], + protected readonly logger?: Logger + ) {} + + /** + * Applies filters to object and drills down into object + * @param val - Value to drill into + * @param depth - the current depth in the object + * @param maxDepth - depth at which to give up + * @returns + */ + applyFilters(val: T, depth = 1, maxDepth = 10): T { + if (depth < maxDepth) { + for (const x of this.actions) { + try { + const result = x.execute(val, depth); + val = result.val; + + // Exit pipeline early if value is not longer actionable. + if (result.exitPipeline) { + break; + } + } catch (err) { + this.logger?.error('sentry.filter.error', { err }); + } + } + + if (val != null && typeof val === 'object') { + Object.values(val).forEach((x) => { + this.applyFilters(x, depth + 1, maxDepth); + }); + } + } + + return val; + } + + abstract filter(data: PiiData): PiiData; +} + +/** + * Defacto Sentry Event Filter. Can be extended and customized as needed. + */ +export class SentryPiiFilter extends FilterBase { + /** + * Creates a new PII Filter for sentry data + * @param actions - Set of filters to apply + */ + constructor(actions: IFilterAction[]) { + super(actions); + } + + /** + * Filter PII from all known sentry fields + */ + public filter(event: ErrorEvent) { + // Target key parts of sentry event structure + this.scrubMessage(event) + .scrubContext(event) + .scrubBreadCrumbs(event) + .scrubRequest(event) + .scrubTags(event) + .scrubException(event) + .scrubExtra(event) + .scrubUser(event); + return event; + } + + protected scrubMessage(event: Event) { + if (event.message) { + event.message = this.applyFilters(event.message); + } + return this; + } + + protected scrubBreadCrumbs(event: Event) { + for (const bc of event.breadcrumbs || []) { + if (bc.message) { + bc.message = this.applyFilters(bc.message); + } + if (bc.data) { + bc.data = this.applyFilters(bc.data); + } + } + return this; + } + + protected scrubRequest(event: Event) { + if (event.request?.headers) { + event.request.headers = this.applyFilters(event.request.headers); + } + + if (event.request?.data) { + event.request.data = this.applyFilters(event.request.data); + } + + if (event.request?.query_string) { + event.request.query_string = this.applyFilters( + event.request.query_string + ); + } + + if (event.request?.env) { + event.request.env = this.applyFilters(event.request.env); + } + + if (event.request?.url) { + event.request.url = this.applyFilters(event.request.url); + } + + if (event.request?.cookies) { + event.request.cookies = this.applyFilters(event.request.cookies); + } + + return this; + } + + protected scrubTags(event: Event) { + if (typeof event.tags?.['url'] === 'string') { + event.tags['url'] = this.applyFilters(event.tags['url']); + } + return this; + } + + protected scrubException(event: Event) { + if (event.exception) { + event.exception = this.applyFilters(event.exception); + } + return this; + } + + protected scrubExtra(event: Event) { + if (event.extra) { + event.extra = this.applyFilters(event.extra); + } + return this; + } + + protected scrubUser(event: Event) { + if (event.user) { + event.user = this.applyFilters(event.user); + } + return this; + } + + protected scrubContext(event: Event) { + if (event.contexts) { + event.contexts = this.applyFilters(event.contexts); + } + return this; + } +} + +/** + * Scrubs PII from SQS Messages + */ +export class SqsMessageFilter extends FilterBase { + /** + * Create a new SqsMessageFilter + * @param actions + */ + constructor(actions: IFilterAction[]) { + super(actions); + } + + /** + * Filter Body of sqs messages + */ + public filter(event: SQS.Message) { + this.filterBody(event); + return event; + } + + protected filterBody(event: SQS.Message) { + event.Body = this.applyFilters(event.Body); + return this; + } +} diff --git a/libs/shared/sentry-utils/src/lib/sentry.types.ts b/libs/shared/sentry-utils/src/lib/sentry.types.ts new file mode 100644 index 00000000000..613a26ac4c0 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/sentry.types.ts @@ -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/. */ + +export type Logger = { + error: (type: string, data?: unknown) => void; + debug: (type: string, data?: unknown) => void; + info: (type: string, data?: unknown) => void; + warn: (type: string, data?: unknown) => void; +}; diff --git a/libs/shared/sentry-utils/src/lib/utils.spec.ts b/libs/shared/sentry-utils/src/lib/utils.spec.ts new file mode 100644 index 00000000000..6d220378a02 --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/utils.spec.ts @@ -0,0 +1,99 @@ +import { cleanUpQueryParam, filterObject } from './utils'; +import * as uuid from 'uuid'; +import { FILTERED } from '@fxa/shared/sentry-utils'; + +describe('filterObject', () => { + it('should be defined', () => { + expect(filterObject).toBeDefined(); + }); + + // Test Sentry QueryParams filtering types + it('should filter array of key/value arrays', () => { + const input = { + type: undefined, + extra: { + foo: uuid.v4().replace(/-/g, ''), + baz: uuid.v4().replace(/-/g, ''), + bar: 'fred', + }, + }; + const expected = { + extra: { + foo: FILTERED, + baz: FILTERED, + bar: 'fred', + }, + }; + const output = filterObject(input); + expect(output).toEqual(expected); + }); + + it('should filter an object of key/value pairs', () => { + const input = { + type: undefined, + extra: { + foo: uuid.v4().replace(/-/g, ''), + baz: uuid.v4().replace(/-/g, ''), + bar: 'fred', + }, + }; + const expected = { + extra: { + foo: FILTERED, + baz: FILTERED, + bar: 'fred', + }, + }; + const output = filterObject(input); + expect(output).toEqual(expected); + }); + + it('should skip nested arrays that are not valid key/value arrays', () => { + const input = { + type: undefined, + extra: { + foo: uuid.v4().replace(/-/g, ''), + bar: 'fred', + fizz: ['buzz', 'parrot'], + }, + }; + const expected = { + extra: { + foo: FILTERED, + bar: 'fred', + fizz: ['buzz', 'parrot'], + }, + }; + const output = filterObject(input); + expect(output).toEqual(expected); + }); +}); + +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); + }); +}); diff --git a/libs/shared/sentry-utils/src/lib/utils.ts b/libs/shared/sentry-utils/src/lib/utils.ts new file mode 100644 index 00000000000..15e2f5efb9e --- /dev/null +++ b/libs/shared/sentry-utils/src/lib/utils.ts @@ -0,0 +1,93 @@ +/* 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 { ErrorEvent } from '@sentry/types'; +import { CommonPiiActions } from './pii/filter-actions'; +import { SentryPiiFilter } from './pii/filters'; + +export function tagFxaName(data: any, name?: string) { + data.tags = data.tags || {}; + data.tags['fxa.name'] = name || 'unknown'; + return data; +} + +const piiFilter = new SentryPiiFilter([ + CommonPiiActions.breadthFilter, + CommonPiiActions.depthFilter, + CommonPiiActions.piiKeys, + CommonPiiActions.emailValues, + CommonPiiActions.tokenValues, + CommonPiiActions.ipV4Values, + CommonPiiActions.ipV6Values, + CommonPiiActions.urlUsernamePassword, +]); + +/** + * Filters all of an objects string properties to remove tokens. + * + * @param event Sentry ErrorEvent + */ +export function filterObject(event: ErrorEvent) { + return piiFilter.filter(event); +} + +/** + * Filter potential PII from a sentry event. + * + * - Limits depth data beyond 5 levels + * - Filters out pii keys, See CommonPiiActions.piiKeys for more details + * - Filters out strings that look like emails addresses + * - Filters out strings that look like tokens value (32 char length alphanumeric values) + * - Filters out strings that look like ip addresses (v4/v6) + * - Filters out urls with user name / password data + * @param event A sentry event + * @returns a sanitized sentry event + */ +export function filterSentryEvent( + event: ErrorEvent, + _hint: unknown +): ErrorEvent { + return piiFilter.filter(event); +} + +/** + * 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; +} diff --git a/libs/shared/sentry-utils/tsconfig.json b/libs/shared/sentry-utils/tsconfig.json new file mode 100644 index 00000000000..8122543a9ab --- /dev/null +++ b/libs/shared/sentry-utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/sentry-utils/tsconfig.lib.json b/libs/shared/sentry-utils/tsconfig.lib.json new file mode 100644 index 00000000000..4befa7f0990 --- /dev/null +++ b/libs/shared/sentry-utils/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/shared/sentry-utils/tsconfig.spec.json b/libs/shared/sentry-utils/tsconfig.spec.json new file mode 100644 index 00000000000..9740aefe5f8 --- /dev/null +++ b/libs/shared/sentry-utils/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/libs/shared/sentry/src/lib/pii/filter-actions.ts b/libs/shared/sentry/src/lib/pii/filter-actions.ts index 984bb7254db..c8f0120f13e 100644 --- a/libs/shared/sentry/src/lib/pii/filter-actions.ts +++ b/libs/shared/sentry/src/lib/pii/filter-actions.ts @@ -245,9 +245,13 @@ export class EmailFilter extends PiiRegexFilter { value.replace(this.regex, this.replaceWith) ); } + screenY; } if (url.pathname) { - url.pathname = url.pathname.replace(this.regex, this.replaceWith); + // substring of 1000 fixes this code ql warning: https://github.com/mozilla/fxa/security/code-scanning/244 + url.pathname = url.pathname + .substring(1000) + .replace(this.regex, this.replaceWith); } try { val = decodeURI(url.toString()); @@ -264,7 +268,9 @@ export class EmailFilter extends PiiRegexFilter { this.encode.forEach((x, i) => { val = val.replace(x, this.decode[i]); }); - val = val.replace(this.regex, this.replaceWith); + + // substring of 1000 fixes this code ql warning: https://github.com/mozilla/fxa/security/code-scanning/244 + val = val.substring(0, 1000).replace(this.regex, this.replaceWith); this.decode.forEach((x, i) => { val = val.replace(x, this.encode[i]); }); diff --git a/package.json b/package.json index f98fb6b551a..ad09f8af3b0 100644 --- a/package.json +++ b/package.json @@ -60,23 +60,16 @@ "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^10.4.1", "@nestjs/schedule": "^4.0.1", - "@opentelemetry/api": "^1.8.0", - "@opentelemetry/auto-instrumentations-node": "^0.44.0", - "@opentelemetry/context-zone": "^1.23.0", - "@opentelemetry/context-zone-peer-dep": "^1.23.0", - "@opentelemetry/exporter-jaeger": "^1.23.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.50.0", - "@opentelemetry/instrumentation-document-load": "^0.37.0", - "@opentelemetry/instrumentation-fetch": "^0.50.0", - "@opentelemetry/instrumentation-user-interaction": "^0.37.0", - "@opentelemetry/instrumentation-xml-http-request": "^0.50.0", - "@opentelemetry/sdk-trace-base": "^1.23.0", - "@opentelemetry/sdk-trace-node": "^1.23.0", - "@opentelemetry/sdk-trace-web": "^1.23.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.49.0", + "@opentelemetry/sdk-trace-base": "^1.26.0", + "@opentelemetry/sdk-trace-node": "^1.26.0", + "@opentelemetry/sdk-trace-web": "^1.26.0", "@paypal/react-paypal-js": "^8.7.0", "@radix-ui/react-form": "^0.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@sentry/browser": "^8.35.0", + "@sentry/nestjs": "^8.35.0", "@sentry/nextjs": "^8.35.0", "@sentry/node": "^8.35.0", "@sentry/opentelemetry": "^8.35.0", @@ -179,6 +172,7 @@ "@nx/react": "19.5.3", "@nx/storybook": "19.5.3", "@nx/workspace": "19.5.3", + "@opentelemetry/semantic-conventions": "^1.27.0", "@storybook/addon-essentials": "^7.6.15", "@storybook/addon-styling": "^1.3.7", "@storybook/core-common": "^7.4.5", diff --git a/packages/fxa-admin-server/package.json b/packages/fxa-admin-server/package.json index 4e90bf67a54..dcf92f003f4 100644 --- a/packages/fxa-admin-server/package.json +++ b/packages/fxa-admin-server/package.json @@ -73,7 +73,7 @@ "pm2": "^5.4.2", "supertest": "^7.0.0", "tailwindcss": "3.4.3", - "ts-jest": "^29.2.3", + "ts-jest": "^29.2.5", "typescript": "^5.5.3", "yargs": "^17.0.1" }, diff --git a/packages/fxa-admin-server/src/app.module.ts b/packages/fxa-admin-server/src/app.module.ts index 664237d12ab..3c1c3f15d62 100644 --- a/packages/fxa-admin-server/src/app.module.ts +++ b/packages/fxa-admin-server/src/app.module.ts @@ -5,13 +5,10 @@ import { HealthModule } from 'fxa-shared/nestjs/health/health.module'; import { LoggerModule } from 'fxa-shared/nestjs/logger/logger.module'; import { MetricsFactory } from 'fxa-shared/nestjs/metrics.service'; -import { - createContext, - SentryPlugin, -} from 'fxa-shared/nestjs/sentry/sentry.plugin'; import { getVersionInfo } from 'fxa-shared/nestjs/version'; import { join } from 'path'; - +import { APP_FILTER } from '@nestjs/core'; +import { SentryGlobalGraphQLFilter, SentryModule } from '@sentry/nestjs/setup'; import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { LegacyStatsDProvider } from '@fxa/shared/metrics/statsd'; import { MozLoggerService } from '@fxa/shared/mozlog'; @@ -46,6 +43,7 @@ const version = getVersionInfo(__dirname); BackendModule, DatabaseModule, EventLoggingModule, + SentryModule.forRoot(), SubscriptionModule, NewslettersModule, GqlModule, @@ -62,8 +60,6 @@ const version = getVersionInfo(__dirname); definitions: { path: join(process.cwd(), 'src/graphql.ts'), }, - context: ({ req, connection }: any) => - createContext({ req, connection }), fieldResolverEnhancers: ['guards'], }), }), @@ -84,7 +80,6 @@ const version = getVersionInfo(__dirname); provide: APP_GUARD, useClass: UserGroupGuard, }, - SentryPlugin, { provide: LOGGER_PROVIDER, useClass: MozLoggerService, @@ -92,6 +87,10 @@ const version = getVersionInfo(__dirname); LegacyNotifierServiceProvider, LegacyNotifierSnsFactory, LegacyStatsDProvider, + { + provide: APP_FILTER, + useClass: SentryGlobalGraphQLFilter, + }, ], }) export class AppModule {} diff --git a/packages/fxa-admin-server/src/config/index.ts b/packages/fxa-admin-server/src/config/index.ts index bc1bf5a1131..3877810e528 100644 --- a/packages/fxa-admin-server/src/config/index.ts +++ b/packages/fxa-admin-server/src/config/index.ts @@ -6,7 +6,7 @@ import convict from 'convict'; import fs from 'fs'; import { makeMySQLConfig, makeRedisConfig } from 'fxa-shared/db/config'; import { GuardConfig, USER_EMAIL_HEADER } from 'fxa-shared/guards'; -import { tracingConfig } from 'fxa-shared/tracing/config'; +import { tracingConfig } from '@fxa/shared/otel'; import path from 'path'; import { CloudTasksConvictConfigFactory } from '@fxa/shared/cloud-tasks'; diff --git a/packages/fxa-admin-server/src/gql/account/account.resolver.ts b/packages/fxa-admin-server/src/gql/account/account.resolver.ts index a708643d6f1..1f9ac47eab1 100644 --- a/packages/fxa-admin-server/src/gql/account/account.resolver.ts +++ b/packages/fxa-admin-server/src/gql/account/account.resolver.ts @@ -15,6 +15,7 @@ import { Resolver, Root, } from '@nestjs/graphql'; +import { SentryTraced } from '@sentry/nestjs'; import AuthClient from 'fxa-auth-client'; import { ClientFormatter, @@ -144,6 +145,7 @@ export class AccountResolver { @Features(AdminPanelFeature.AccountSearch) @Query((returns) => AccountType, { nullable: true }) + @SentryTraced('accountByEmail') public async accountByEmail( @Args('email', { nullable: false }) email: string, @Args('autoCompleted', { nullable: false }) autoCompleted: boolean, diff --git a/packages/fxa-admin-server/src/gql/gql.module.ts b/packages/fxa-admin-server/src/gql/gql.module.ts index 1bc09c8b232..a6241d79c1f 100644 --- a/packages/fxa-admin-server/src/gql/gql.module.ts +++ b/packages/fxa-admin-server/src/gql/gql.module.ts @@ -18,6 +18,8 @@ import { SubscriptionModule } from '../subscriptions/subscriptions.module'; import { AccountResolver } from './account/account.resolver'; import { EmailBounceResolver } from './email-bounce/email-bounce.resolver'; import { RelyingPartyResolver } from './relying-party/relying-party.resolver'; +import { APP_FILTER } from '@nestjs/core'; +import { SentryGlobalGraphQLFilter } from '@sentry/nestjs/setup'; @Module({ imports: [ @@ -38,6 +40,10 @@ import { RelyingPartyResolver } from './relying-party/relying-party.resolver'; useClass: MozLoggerService, }, RelyingPartyResolver, + { + provide: APP_FILTER, + useClass: SentryGlobalGraphQLFilter, + }, ], }) export class GqlModule {} diff --git a/packages/fxa-admin-server/src/main.ts b/packages/fxa-admin-server/src/main.ts index 1938f2e7243..2a5abf186e7 100644 --- a/packages/fxa-admin-server/src/main.ts +++ b/packages/fxa-admin-server/src/main.ts @@ -9,24 +9,14 @@ import './monitoring'; import { NestApplicationOptions } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; -import { SentryInterceptor } from 'fxa-shared/nestjs/sentry/sentry.interceptor'; -import { initTracing } from 'fxa-shared/tracing/node-tracing'; -import mozLog from 'mozlog'; +import { allowlistGqlQueries } from 'fxa-shared/nestjs/gql/gql-allowlist'; import helmet from 'helmet'; - import { AppModule } from './app.module'; import Config, { AppConfig } from './config'; -import { allowlistGqlQueries } from 'fxa-shared/nestjs/gql/gql-allowlist'; const appConfig = Config.getProperties() as AppConfig; async function bootstrap() { - // Initialize tracing first - initTracing( - appConfig.tracing, - mozLog(Config.getProperties().log)(Config.getProperties().log.app) - ); - const nestConfig: NestApplicationOptions = {}; if (appConfig.env !== 'development') { nestConfig.logger = false; @@ -50,9 +40,6 @@ async function bootstrap() { app.set('trust proxy', true); } - // Add sentry as error reporter - app.useGlobalInterceptors(new SentryInterceptor()); - // Starts listening for shutdown hooks app.enableShutdownHooks(); diff --git a/packages/fxa-admin-server/src/monitoring.ts b/packages/fxa-admin-server/src/monitoring.ts index 77fe9d6e66a..20be8301c5e 100644 --- a/packages/fxa-admin-server/src/monitoring.ts +++ b/packages/fxa-admin-server/src/monitoring.ts @@ -2,17 +2,31 @@ * 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/nestjs'; import Config from './config'; import mozLog from 'mozlog'; -import { initMonitoring } from 'fxa-shared/monitoring'; +import { initTracing } from '@fxa/shared/otel'; +import { initSentry } from '@fxa/shared/sentry-nest'; import { version } from '../package.json'; const config = Config.getProperties(); -const log = mozLog(config.log)(config.log.app); -initMonitoring({ - log, - config: { +const logger = mozLog(config.log)(config.log.app); + +initTracing(config.tracing, logger); +initSentry( + { ...config, release: version, + integrations: [Sentry.extraErrorDataIntegration({ depth: 5 })], }, -}); + { + warn: (type: string, data: unknown) => + logger.warn(type.replace(/ /g, '-'), data as any), + debug: (type: string, data: unknown) => + logger.debug(type.replace(/ /g, '-'), data as any), + info: (type: string, data: unknown) => + logger.info(type.replace(/ /g, '-'), data as any), + error: (type: string, data: unknown) => + logger.error(type.replace(/ /g, '-'), data as any), + } +); diff --git a/packages/fxa-admin-server/src/scripts/audit-tokens.spec.ts b/packages/fxa-admin-server/src/scripts/audit-tokens.spec.ts index a343a9bbe8e..2a8b508dcde 100644 --- a/packages/fxa-admin-server/src/scripts/audit-tokens.spec.ts +++ b/packages/fxa-admin-server/src/scripts/audit-tokens.spec.ts @@ -25,7 +25,6 @@ const exec = util.promisify(require('node:child_process').exec); const cwd = path.resolve(__dirname, '..'); describe('#integration - scripts/audit-tokens', () => { - let fxaDb: Knex; let fxaOauthDb: Knex; let fxaProfileDb: Knex; const uid = 'f9916686c226415abd06ae550f073cea'; @@ -37,7 +36,6 @@ describe('#integration - scripts/audit-tokens', () => { // Create DB connections. Note that in the real world, these would be // separate databases instances. During local testing, we use the same // instance for all databases. - fxaDb = bindKnex(config.database.fxa); fxaProfileDb = bindKnex(config.database.profile, TargetDB.profile); fxaOauthDb = bindKnex(config.database.fxa_oauth, TargetDB.oauth); diff --git a/packages/fxa-admin-server/src/subscriptions/appstore.service.ts b/packages/fxa-admin-server/src/subscriptions/appstore.service.ts index 94c175f77f2..b248e3946f2 100644 --- a/packages/fxa-admin-server/src/subscriptions/appstore.service.ts +++ b/packages/fxa-admin-server/src/subscriptions/appstore.service.ts @@ -5,7 +5,7 @@ import { LOGGER_PROVIDER } from '@fxa/shared/log'; import { MozLoggerService } from '@fxa/shared/mozlog'; import { Firestore } from '@google-cloud/firestore'; -import { Inject, Injectable, LoggerService } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { AppStoreHelper } from 'fxa-shared/payments/iap/apple-app-store/app-store-helper'; import { PurchaseManager } from 'fxa-shared/payments/iap/apple-app-store/purchase-manager'; diff --git a/packages/fxa-admin-server/src/subscriptions/stripe.service.spec.ts b/packages/fxa-admin-server/src/subscriptions/stripe.service.spec.ts index e9ce9990322..f86a1db3999 100644 --- a/packages/fxa-admin-server/src/subscriptions/stripe.service.spec.ts +++ b/packages/fxa-admin-server/src/subscriptions/stripe.service.spec.ts @@ -178,8 +178,9 @@ describe('Stripe Service', () => { it('creates manage subscription link', async () => { const customerId = 'customer-123'; - const manageSessionUrl = - 'https://dashboard.stripe.com/customers/customer-123'; + const manageSessionUrl = service.isTestingApi + ? 'https://dashboard.stripe.com/test/customers/customer-123' + : 'https://dashboard.stripe.com/customers/customer-123'; const result1 = await service.createManageSubscriptionLink(customerId); diff --git a/packages/fxa-admin-server/src/subscriptions/stripe.service.ts b/packages/fxa-admin-server/src/subscriptions/stripe.service.ts index 5916f7eff3f..21ee6fcbf5a 100644 --- a/packages/fxa-admin-server/src/subscriptions/stripe.service.ts +++ b/packages/fxa-admin-server/src/subscriptions/stripe.service.ts @@ -38,7 +38,6 @@ export const StripeFactory: Provider = { provide: 'STRIPE', useFactory: (configService: ConfigService) => { const stripeConfig = configService.get('subscriptions'); - const env = configService.get('env') || ''; const stripe = new Stripe(stripeConfig.stripeApiKey, { apiVersion: '2024-04-10', maxNetworkRetries: 3, @@ -114,7 +113,7 @@ export class StripeFirestoreService extends StripeFirestore { @Injectable() export class StripeService extends StripeHelper { - protected readonly isTestingApi: boolean; + public readonly isTestingApi: boolean; public override readonly stripe: Stripe; protected override readonly stripeFirestore: StripeFirestore; protected override readonly paymentConfigManager?: diff --git a/packages/fxa-admin-server/tsconfig.build.json b/packages/fxa-admin-server/tsconfig.build.json index 6c8ad0f6149..37507cec405 100644 --- a/packages/fxa-admin-server/tsconfig.build.json +++ b/packages/fxa-admin-server/tsconfig.build.json @@ -18,10 +18,13 @@ "@fxa/shared/l10n": ["libs/shared/l10n/src/index"], "@fxa/shared/log": ["libs/shared/log/src/index"], "@fxa/shared/metrics/statsd": ["libs/shared/metrics/statsd/src/index"], + "@fxa/shared/otel": ["libs/shared/otel/src/index"], "@fxa/shared/pem-jwk": ["libs/shared/pem-jwk/src/index"], "@fxa/vendored/typesafe-node-firestore": ["libs/vendored/typesafe-node-firestore/src/index"], "@fxa/shared/notifier": ["libs/shared/notifier/src/index"], - "@fxa/shared/mozlog": ["libs/shared/mozlog/src/index"] + "@fxa/shared/mozlog": ["libs/shared/mozlog/src/index"], + "@fxa/shared/sentry-nest": ["libs/shared/sentry-nest/src/index"], + "@fxa/shared/sentry-utils": ["libs/shared/sentry-utils/src/index"] } } } diff --git a/packages/fxa-shared/test/tracing/exporters.ts b/packages/fxa-shared/test/tracing/exporters.ts index e1543d27e00..80fcfd3576a 100644 --- a/packages/fxa-shared/test/tracing/exporters.ts +++ b/packages/fxa-shared/test/tracing/exporters.ts @@ -7,7 +7,7 @@ import { BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace-node'; import { expect } from 'chai'; import { addConsoleExporter } from '../../tracing/exporters/fxa-console'; import { addGcpTraceExporter } from '../../tracing/exporters/fxa-gcp'; @@ -16,7 +16,6 @@ import sinon from 'sinon'; import { TracingOpts } from '../../tracing/config'; import { addExporter } from '../../tracing/exporters/exporters'; import { checkDuration } from '../../tracing/exporters/util'; -import { addSentryTraceExporter } from '../../tracing/exporters/fxa-sentry'; describe('tracing exports', () => { const sandbox = sinon.createSandbox(); @@ -66,11 +65,6 @@ describe('tracing exports', () => { sinon.assert.calledOnce(addSpanProcessorSpy); }); - it('creates sentry exporter', () => { - expect(addSentryTraceExporter(opts, provider)).to.exist; - sinon.assert.calledOnce(addSpanProcessorSpy); - }); - describe('exporter', () => { it('adds simple span processor', () => { const exporter = new ConsoleSpanExporter(); @@ -123,11 +117,6 @@ describe('tracing exports', () => { expect(addOtlpTraceExporter(opts, provider)).to.not.exist; sinon.assert.notCalled(addSpanProcessorSpy); }); - - it('creates sentry exporter', () => { - expect(addSentryTraceExporter(opts, provider)).to.not.exist; - sinon.assert.notCalled(addSpanProcessorSpy); - }); }); describe('util', () => { diff --git a/packages/fxa-shared/tracing/exporters/exporters.ts b/packages/fxa-shared/tracing/exporters/exporters.ts index ec4e63387cf..6ba87abbd4c 100644 --- a/packages/fxa-shared/tracing/exporters/exporters.ts +++ b/packages/fxa-shared/tracing/exporters/exporters.ts @@ -2,17 +2,17 @@ * 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 { TracingOpts } from '../config'; import { - BasicTracerProvider, + NodeTracerProvider, BatchSpanProcessor, SimpleSpanProcessor, SpanExporter, -} from '@opentelemetry/sdk-trace-base'; -import { TracingOpts } from '../config'; +} from '@opentelemetry/sdk-trace-node'; export function addExporter( opts: TracingOpts, - provider: BasicTracerProvider, + provider: NodeTracerProvider, exporter: SpanExporter ) { const processor = opts.batchProcessor diff --git a/packages/fxa-shared/tracing/exporters/fxa-console.ts b/packages/fxa-shared/tracing/exporters/fxa-console.ts index f87a731f75a..60fdb78ac0f 100644 --- a/packages/fxa-shared/tracing/exporters/fxa-console.ts +++ b/packages/fxa-shared/tracing/exporters/fxa-console.ts @@ -7,7 +7,7 @@ import { BasicTracerProvider, ConsoleSpanExporter, ReadableSpan, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace-node'; import { TracingOpts } from '../config'; import { TracingPiiFilter } from '../pii-filters'; import { addExporter } from './exporters'; diff --git a/packages/fxa-shared/tracing/exporters/fxa-gcp.ts b/packages/fxa-shared/tracing/exporters/fxa-gcp.ts index a9624b91f0b..6881c04ec7a 100644 --- a/packages/fxa-shared/tracing/exporters/fxa-gcp.ts +++ b/packages/fxa-shared/tracing/exporters/fxa-gcp.ts @@ -10,12 +10,13 @@ import { ExportResult } from '@opentelemetry/core'; import { BasicTracerProvider, ReadableSpan, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace-node'; import { TracingOpts } from '../config'; import { TracingPiiFilter } from '../pii-filters'; import { addExporter } from './exporters'; import { checkDuration } from './util'; import { ILogger } from '../../log'; +import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; /** Gcp exporter customized for FxA */ @@ -41,7 +42,7 @@ export class FxaGcpTraceExporter extends GcpTraceExporter { export function addGcpTraceExporter( opts: TracingOpts, - provider: BasicTracerProvider, + provider: NodeTracerProvider, filter?: TracingPiiFilter, logger?: ILogger ) { diff --git a/packages/fxa-shared/tracing/exporters/fxa-otlp.ts b/packages/fxa-shared/tracing/exporters/fxa-otlp.ts index d0035bf160a..5e497ed861c 100644 --- a/packages/fxa-shared/tracing/exporters/fxa-otlp.ts +++ b/packages/fxa-shared/tracing/exporters/fxa-otlp.ts @@ -8,7 +8,7 @@ import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base'; import { BasicTracerProvider, ReadableSpan, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace-node'; import { TracingOpts } from '../config'; import { TracingPiiFilter } from '../pii-filters'; import { addExporter } from './exporters'; diff --git a/packages/fxa-shared/tracing/exporters/fxa-sentry.ts b/packages/fxa-shared/tracing/exporters/fxa-sentry.ts deleted file mode 100644 index fb07543588c..00000000000 --- a/packages/fxa-shared/tracing/exporters/fxa-sentry.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; -import { TracingOpts } from '../config'; -import { SentrySpanProcessor, SentryPropagator } from '@sentry/opentelemetry'; -import { TracingPiiFilter } from '../pii-filters'; -import { ILogger } from '../../log'; - -/** - * Important must be called after calling Sentry.init()! - * @returns object containing processor, propagator, context manager and sampler, needed to - * relay otel traces to sentry. - */ -export function addSentryTraceExporter( - opts: TracingOpts, - provider: NodeTracerProvider, - _filter?: TracingPiiFilter, - logger?: ILogger -) { - if (!opts.sentry?.enabled) { - return; - } - - // TBD: We might need to create override classes for SentrySpanProcessor or SentryPropagator - // in oder to filter out any potential PII in the context. Let's keep an eye on this, since - // it's not 100% obvious if this will be needed until we see more data in sentry. - - logger?.debug('Adding Sentry Trace Exporter'); - - // Note: Source for SentryPropagator can be found here: - // https://github.com/getsentry/sentry-javascript/tree/master/packages/opentelemetry-node - const spanProcessor = new SentrySpanProcessor(); - const propagator = new SentryPropagator(); - - // Register sentry implementations - provider.addSpanProcessor(spanProcessor); - provider.register({ - propagator: propagator, - }); - - // Sentry doesn't have a 'true exporter' rather it piggy backs on the propagator, which - // is close enough to an exporter... - return propagator; -} diff --git a/packages/fxa-shared/tracing/exporters/util.ts b/packages/fxa-shared/tracing/exporters/util.ts index 082067b2f0a..cd077d2da2f 100644 --- a/packages/fxa-shared/tracing/exporters/util.ts +++ b/packages/fxa-shared/tracing/exporters/util.ts @@ -2,7 +2,7 @@ * 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 { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-node'; /** * Checks, fixes, and flags bad duration state on spans. diff --git a/packages/fxa-shared/tracing/node-tracing.ts b/packages/fxa-shared/tracing/node-tracing.ts index 8ccd9884975..0d48509d33d 100644 --- a/packages/fxa-shared/tracing/node-tracing.ts +++ b/packages/fxa-shared/tracing/node-tracing.ts @@ -13,7 +13,6 @@ import { addConsoleExporter } from './exporters/fxa-console'; import { addGcpTraceExporter } from './exporters/fxa-gcp'; import { addOtlpTraceExporter } from './exporters/fxa-otlp'; import { createPiiFilter } from './pii-filters'; -import { addSentryTraceExporter } from './exporters/fxa-sentry'; import { createNodeProvider } from './providers/node-provider'; const log_type = 'node-tracing'; @@ -38,7 +37,6 @@ export class NodeTracingInitializer { this.provider = createNodeProvider(this.opts); const filter = createPiiFilter(!!this.opts?.filterPii, this.logger); - addSentryTraceExporter(opts, this.provider, filter, this.logger); addGcpTraceExporter(opts, this.provider, filter, this.logger); addOtlpTraceExporter(opts, this.provider, undefined, filter, this.logger); addConsoleExporter(opts, this.provider, filter); diff --git a/packages/fxa-shared/tracing/providers/node-provider.ts b/packages/fxa-shared/tracing/providers/node-provider.ts index f636f80f6f0..13689f25c82 100644 --- a/packages/fxa-shared/tracing/providers/node-provider.ts +++ b/packages/fxa-shared/tracing/providers/node-provider.ts @@ -6,7 +6,7 @@ import { Resource } from '@opentelemetry/resources'; import { ParentBasedSampler, TraceIdRatioBasedSampler, -} from '@opentelemetry/sdk-trace-base'; +} from '@opentelemetry/sdk-trace-node'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import { TracingOpts } from '../config'; diff --git a/tsconfig.base.json b/tsconfig.base.json index e077d1a5a9d..08fcbf00bd8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -31,7 +31,9 @@ "@fxa/payments/eligibility": ["libs/payments/eligibility/src/index.ts"], "@fxa/payments/legacy": ["libs/payments/legacy/src/index.ts"], "@fxa/payments/metrics": ["libs/payments/metrics/src/index.ts"], - "@fxa/payments/metrics/provider": ["libs/payments/metrics/src/provider.ts"], + "@fxa/payments/metrics/provider": [ + "libs/payments/metrics/src/provider.ts" + ], "@fxa/payments/paypal": ["libs/payments/paypal/src/index.ts"], "@fxa/payments/stripe": ["libs/payments/stripe/src/index.ts"], "@fxa/payments/ui": ["libs/payments/ui/src/index.ts"], @@ -65,11 +67,17 @@ "@fxa/shared/metrics/statsd": ["libs/shared/metrics/statsd/src/index.ts"], "@fxa/shared/mozlog": ["libs/shared/mozlog/src/index.ts"], "@fxa/shared/notifier": ["libs/shared/notifier/src/index.ts"], + "@fxa/shared/otel": ["libs/shared/otel/src/index.ts"], "@fxa/shared/otp": ["libs/shared/otp/src/index.ts"], "@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/shared/sentry-browser": ["libs/shared/sentry-browser/src/index.ts"], + "@fxa/shared/sentry-nest": ["libs/shared/sentry-nest/src/index.ts"], + "@fxa/shared/sentry-next": ["libs/shared/sentry-next/src/index.ts"], + "@fxa/shared/sentry-node": ["libs/shared/sentry-node/src/index.ts"], + "@fxa/shared/sentry-utils": ["libs/shared/sentry-utils/src/index.ts"], "@fxa/vendored/common-password-list": [ "libs/vendored/common-password-list/src/index.ts" ], diff --git a/yarn.lock b/yarn.lock index 109b150661f..481620cef03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10705,7 +10705,7 @@ __metadata: languageName: node linkType: hard -"@grpc/grpc-js@npm:^1.1.8, @grpc/grpc-js@npm:^1.7.1": +"@grpc/grpc-js@npm:^1.1.8": version: 1.9.15 resolution: "@grpc/grpc-js@npm:1.9.15" dependencies: @@ -10725,6 +10725,16 @@ __metadata: languageName: node linkType: hard +"@grpc/grpc-js@npm:^1.7.1": + version: 1.12.2 + resolution: "@grpc/grpc-js@npm:1.12.2" + dependencies: + "@grpc/proto-loader": ^0.7.13 + "@js-sdsl/ordered-map": ^4.4.2 + checksum: ee51317f92ec5331931b68bf3a4e485a6f7a715609b8aaa29573b3a2b211d5f37c48cda084e25e2bf21142925f8ec9ca49a9e613cbbe06b73785150825d171df + languageName: node + linkType: hard + "@grpc/proto-loader@npm:^0.7.0": version: 0.7.5 resolution: "@grpc/proto-loader@npm:0.7.5" @@ -14407,16 +14417,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api-logs@npm:0.50.0, @opentelemetry/api-logs@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/api-logs@npm:0.50.0" - dependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 5d4d9d448d1dc3a74879a19d5d24b9aecfd180e05acc622e25e5ca1bd0ad2c27b5541e101e474f2870e6470e148a7bad3c1b041d5a41181ebcde1f38a1ee6feb - languageName: node - linkType: hard - -"@opentelemetry/api-logs@npm:0.52.1": +"@opentelemetry/api-logs@npm:0.52.1, @opentelemetry/api-logs@npm:^0.52.0": version: 0.52.1 resolution: "@opentelemetry/api-logs@npm:0.52.1" dependencies: @@ -14455,73 +14456,78 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:^1.8.0": - version: 1.8.0 - resolution: "@opentelemetry/api@npm:1.8.0" - checksum: 0e32079975f05bee6de2ad8ade097f0afdc63f462c76550150fce2444c73ab92aaf851ac85e638b6e3b269da6640ac7e63f33913a0fd7df9f9beec2e100759df +"@opentelemetry/auto-instrumentations-node@npm:^0.49.0": + version: 0.49.2 + resolution: "@opentelemetry/auto-instrumentations-node@npm:0.49.2" + dependencies: + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation-amqplib": ^0.41.0 + "@opentelemetry/instrumentation-aws-lambda": ^0.43.0 + "@opentelemetry/instrumentation-aws-sdk": ^0.43.1 + "@opentelemetry/instrumentation-bunyan": ^0.40.0 + "@opentelemetry/instrumentation-cassandra-driver": ^0.40.0 + "@opentelemetry/instrumentation-connect": ^0.38.0 + "@opentelemetry/instrumentation-cucumber": ^0.8.0 + "@opentelemetry/instrumentation-dataloader": ^0.11.0 + "@opentelemetry/instrumentation-dns": ^0.38.0 + "@opentelemetry/instrumentation-express": ^0.41.1 + "@opentelemetry/instrumentation-fastify": ^0.38.0 + "@opentelemetry/instrumentation-fs": ^0.14.0 + "@opentelemetry/instrumentation-generic-pool": ^0.38.1 + "@opentelemetry/instrumentation-graphql": ^0.42.0 + "@opentelemetry/instrumentation-grpc": ^0.52.0 + "@opentelemetry/instrumentation-hapi": ^0.40.0 + "@opentelemetry/instrumentation-http": ^0.52.0 + "@opentelemetry/instrumentation-ioredis": ^0.42.0 + "@opentelemetry/instrumentation-kafkajs": ^0.2.0 + "@opentelemetry/instrumentation-knex": ^0.39.0 + "@opentelemetry/instrumentation-koa": ^0.42.0 + "@opentelemetry/instrumentation-lru-memoizer": ^0.39.0 + "@opentelemetry/instrumentation-memcached": ^0.38.0 + "@opentelemetry/instrumentation-mongodb": ^0.46.0 + "@opentelemetry/instrumentation-mongoose": ^0.41.0 + "@opentelemetry/instrumentation-mysql": ^0.40.0 + "@opentelemetry/instrumentation-mysql2": ^0.40.0 + "@opentelemetry/instrumentation-nestjs-core": ^0.39.0 + "@opentelemetry/instrumentation-net": ^0.38.0 + "@opentelemetry/instrumentation-pg": ^0.43.0 + "@opentelemetry/instrumentation-pino": ^0.41.0 + "@opentelemetry/instrumentation-redis": ^0.41.0 + "@opentelemetry/instrumentation-redis-4": ^0.41.1 + "@opentelemetry/instrumentation-restify": ^0.40.0 + "@opentelemetry/instrumentation-router": ^0.39.0 + "@opentelemetry/instrumentation-socket.io": ^0.41.0 + "@opentelemetry/instrumentation-tedious": ^0.13.0 + "@opentelemetry/instrumentation-undici": ^0.5.0 + "@opentelemetry/instrumentation-winston": ^0.39.0 + "@opentelemetry/resource-detector-alibaba-cloud": ^0.29.0 + "@opentelemetry/resource-detector-aws": ^1.6.0 + "@opentelemetry/resource-detector-azure": ^0.2.10 + "@opentelemetry/resource-detector-container": ^0.4.0 + "@opentelemetry/resource-detector-gcp": ^0.29.10 + "@opentelemetry/resources": ^1.24.0 + "@opentelemetry/sdk-node": ^0.52.0 + peerDependencies: + "@opentelemetry/api": ^1.4.1 + checksum: 383a7cac4994a48e5ca918fc84dddeb25f35c1744e1e6da186cd85b58e5b248c69d3ec92719828401877aec05913c94c51d987b5ec46d7965c9b99a1cd31af80 languageName: node linkType: hard -"@opentelemetry/auto-instrumentations-node@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/auto-instrumentations-node@npm:0.44.0" - dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/instrumentation-amqplib": ^0.36.0 - "@opentelemetry/instrumentation-aws-lambda": ^0.40.0 - "@opentelemetry/instrumentation-aws-sdk": ^0.40.0 - "@opentelemetry/instrumentation-bunyan": ^0.37.0 - "@opentelemetry/instrumentation-cassandra-driver": ^0.37.0 - "@opentelemetry/instrumentation-connect": ^0.35.0 - "@opentelemetry/instrumentation-cucumber": ^0.5.0 - "@opentelemetry/instrumentation-dataloader": ^0.8.0 - "@opentelemetry/instrumentation-dns": ^0.35.0 - "@opentelemetry/instrumentation-express": ^0.37.0 - "@opentelemetry/instrumentation-fastify": ^0.35.0 - "@opentelemetry/instrumentation-fs": ^0.11.0 - "@opentelemetry/instrumentation-generic-pool": ^0.35.0 - "@opentelemetry/instrumentation-graphql": ^0.39.0 - "@opentelemetry/instrumentation-grpc": ^0.50.0 - "@opentelemetry/instrumentation-hapi": ^0.36.0 - "@opentelemetry/instrumentation-http": ^0.50.0 - "@opentelemetry/instrumentation-ioredis": ^0.39.0 - "@opentelemetry/instrumentation-knex": ^0.35.0 - "@opentelemetry/instrumentation-koa": ^0.39.0 - "@opentelemetry/instrumentation-lru-memoizer": ^0.36.0 - "@opentelemetry/instrumentation-memcached": ^0.35.0 - "@opentelemetry/instrumentation-mongodb": ^0.42.0 - "@opentelemetry/instrumentation-mongoose": ^0.37.0 - "@opentelemetry/instrumentation-mysql": ^0.37.0 - "@opentelemetry/instrumentation-mysql2": ^0.37.0 - "@opentelemetry/instrumentation-nestjs-core": ^0.36.0 - "@opentelemetry/instrumentation-net": ^0.35.0 - "@opentelemetry/instrumentation-pg": ^0.40.0 - "@opentelemetry/instrumentation-pino": ^0.37.0 - "@opentelemetry/instrumentation-redis": ^0.38.0 - "@opentelemetry/instrumentation-redis-4": ^0.38.0 - "@opentelemetry/instrumentation-restify": ^0.37.0 - "@opentelemetry/instrumentation-router": ^0.36.0 - "@opentelemetry/instrumentation-socket.io": ^0.38.0 - "@opentelemetry/instrumentation-tedious": ^0.9.0 - "@opentelemetry/instrumentation-winston": ^0.36.0 - "@opentelemetry/resource-detector-alibaba-cloud": ^0.28.8 - "@opentelemetry/resource-detector-aws": ^1.4.1 - "@opentelemetry/resource-detector-container": ^0.3.8 - "@opentelemetry/resource-detector-gcp": ^0.29.8 - "@opentelemetry/resources": ^1.12.0 - "@opentelemetry/sdk-node": ^0.50.0 +"@opentelemetry/context-async-hooks@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/context-async-hooks@npm:1.25.1" peerDependencies: - "@opentelemetry/api": ^1.4.1 - checksum: ce63a473cb8ba9fb8af1fcab678c21d90a8af426887bb2f1cd642015d03c85efcbbace212c327b361921eefca9b2c176af77358d8d8712736ab73ffc0b802f5b + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: fb2ac7381ea8203a1321e2a4989c193b6eede0b0f46bafc150e452ac5fc4645127f0ca66f60e44ff2816032afc68cbe3ab9cf235fbdffb0ad83f484729b70e82 languageName: node linkType: hard -"@opentelemetry/context-async-hooks@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/context-async-hooks@npm:1.23.0" +"@opentelemetry/context-async-hooks@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/context-async-hooks@npm:1.27.0" peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 4dc6c4f816402fe3deb5d43aebd4ceadd8afa8feab2047eed7cc906379fd341686cac8d16bce1c436d15e03b29883bcf73f04d4da005abe318e0b9ec69bdbd23 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: d0fb0dd9e9de9b5404c50b48aa982c096f1f58aa518e472a3e48bd8d404354700132005886a497b7067cae6e813dab374a6c1483e344e53e396274c5b00f2a31 languageName: node linkType: hard @@ -14534,37 +14540,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/context-zone-peer-dep@npm:1.23.0, @opentelemetry/context-zone-peer-dep@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/context-zone-peer-dep@npm:1.23.0" - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - zone.js: ^0.10.2 || ^0.11.0 || ^0.13.0 || ^0.14.0 - checksum: 7f4ba8424030b5df2e0b2d38e7a118d092dd90200a8eab931429d378b77181e30fff145182a2beb3315af2ba00a164c69f30e92ed94867519717a8a4ac4055e7 - languageName: node - linkType: hard - -"@opentelemetry/context-zone@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/context-zone@npm:1.23.0" - dependencies: - "@opentelemetry/context-zone-peer-dep": 1.23.0 - zone.js: ^0.11.0 || ^0.13.0 || ^0.14.0 - checksum: 7530caec154cc2fb19e5befedaf40d18efcca3b43b69602a13e0209a96756cde64d1065ecd6d8f5d18cd1075d72c95cfd1f568d5c386fedb5d797929b82fa111 - languageName: node - linkType: hard - -"@opentelemetry/core@npm:1.13.0": - version: 1.13.0 - resolution: "@opentelemetry/core@npm:1.13.0" - dependencies: - "@opentelemetry/semantic-conventions": 1.13.0 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.5.0" - checksum: a69916bcb710f1241e98a58ac5f5dfbc3372fdcd6cb2a4b2d33cdeb941765ecbdeea029f60f650a5743a56f583b0f06b672566467b89db84a24f1304bf2e5205 - languageName: node - linkType: hard - "@opentelemetry/core@npm:1.15.2, @opentelemetry/core@npm:^1.1.0": version: 1.15.2 resolution: "@opentelemetry/core@npm:1.15.2" @@ -14576,40 +14551,40 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.17.0": - version: 1.17.0 - resolution: "@opentelemetry/core@npm:1.17.0" +"@opentelemetry/core@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/core@npm:1.25.1" dependencies: - "@opentelemetry/semantic-conventions": 1.17.0 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.7.0" - checksum: 8f66bc47f2b9cae429830c91840515d6d70793c27fa139e661a7ae05c503d4a7244b5d52e3526cd32401a5a662775bb04546ca1e3ec20dc7124e6d0bb901f176 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: ba1672fde4a1cfd9b55bf6070db71b808702fe59c4a70cda52a6156b2c813827954a6b4d3c3641283d394ff75a69b6359a0487459b4d26cd7d714ab3d21bc780 languageName: node linkType: hard -"@opentelemetry/core@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/core@npm:1.23.0" +"@opentelemetry/core@npm:1.26.0, @opentelemetry/core@npm:^1.0.0, @opentelemetry/core@npm:^1.25.1": + version: 1.26.0 + resolution: "@opentelemetry/core@npm:1.26.0" dependencies: - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/semantic-conventions": 1.27.0 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 88aa733364c42f90a61a6efc8b5138dcfed4763f3a0692d957d506a6fe49db943169a0631ad762ac7569723faf5eaca092d6590eca1ad8ff77583fb10512a06b + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: e5b06b4d69605927b850109c6b898f00a6a921171b3bf62335a4e00b9a170c1b93ddef6d7f8cc480a551faeaf81074b594f4462a91d4fbc4b313e64ff9ebd717 languageName: node linkType: hard -"@opentelemetry/core@npm:1.26.0, @opentelemetry/core@npm:^1.25.1": - version: 1.26.0 - resolution: "@opentelemetry/core@npm:1.26.0" +"@opentelemetry/core@npm:1.27.0, @opentelemetry/core@npm:^1.25.0, @opentelemetry/core@npm:^1.26.0": + version: 1.27.0 + resolution: "@opentelemetry/core@npm:1.27.0" dependencies: "@opentelemetry/semantic-conventions": 1.27.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: e5b06b4d69605927b850109c6b898f00a6a921171b3bf62335a4e00b9a170c1b93ddef6d7f8cc480a551faeaf81074b594f4462a91d4fbc4b313e64ff9ebd717 + checksum: 33ff551f89f0bb95830c9f9464c43b11adf88882ec1d3a03a5b9afcc89d2aafab33c36cb5047f18667d7929d6ab40ed0121649c42d0105f1cb33ffdca48f8b13 languageName: node linkType: hard -"@opentelemetry/core@npm:1.8.0, @opentelemetry/core@npm:^1.8.0": +"@opentelemetry/core@npm:^1.8.0": version: 1.8.0 resolution: "@opentelemetry/core@npm:1.8.0" dependencies: @@ -14620,102 +14595,76 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:^1.0.0": - version: 1.7.0 - resolution: "@opentelemetry/core@npm:1.7.0" - dependencies: - "@opentelemetry/semantic-conventions": 1.7.0 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.3.0" - checksum: 94fcae57c3c2c3a1cff6311246f32a228b216533449bfcec2f8eb03ea023f0ace4e0929c8cf5145772c6f25263d5f2d5d3485a39ab0ced4e11f5a0fed7497e9c - languageName: node - linkType: hard - -"@opentelemetry/exporter-jaeger@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/exporter-jaeger@npm:1.23.0" - dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 - jaeger-client: ^3.15.0 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: b306e335a2892950afecf06f305878b58422bd226777635267499c9cdbf1b4a85992f8bc285807e24406df8f498db44c159bb84f0513161c41b7ce42f7deb9ba - languageName: node - linkType: hard - -"@opentelemetry/exporter-trace-otlp-grpc@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.50.0" +"@opentelemetry/exporter-trace-otlp-grpc@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.52.1" dependencies: "@grpc/grpc-js": ^1.7.1 - "@opentelemetry/core": 1.23.0 - "@opentelemetry/otlp-grpc-exporter-base": 0.50.0 - "@opentelemetry/otlp-transformer": 0.50.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/otlp-grpc-exporter-base": 0.52.1 + "@opentelemetry/otlp-transformer": 0.52.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: f27189ebf0ae4f417d7e3697a5679805da3544cc60082b89d2d66344a3a4e1941f2f9ab130954ea2718ed32f14201d5679b675d1eb026b7ed5f7b9f817243e57 + checksum: c8f21e7a5802a7089c1f9f01a1f21934da7adf8526f36a16da6ba4f78e1a59e3da7c301070cd6df78d07555babb1853ab23180ea7df02eefea4ece66fccdf3d1 languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-http@npm:0.50.0, @opentelemetry/exporter-trace-otlp-http@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.50.0" +"@opentelemetry/exporter-trace-otlp-http@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.52.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/otlp-exporter-base": 0.50.0 - "@opentelemetry/otlp-transformer": 0.50.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/otlp-exporter-base": 0.52.1 + "@opentelemetry/otlp-transformer": 0.52.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: e0725be8f19f2c37c9b16989ff234183213878dad44f0698fc2b6a815c5979498c3766b0377932197ef051a0914d337c81a486612eac2285a4c9cb8313eefa6b + checksum: fa72c84c35268f500b8fdc4d1b388fbf7275170aa67a2b2712c1e6b0d657e208a1e1d222081f0a1860c3f204f12ff78c2f6fe622d08ea8123410f276883659e2 languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-proto@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.50.0" +"@opentelemetry/exporter-trace-otlp-proto@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.52.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/otlp-exporter-base": 0.50.0 - "@opentelemetry/otlp-proto-exporter-base": 0.50.0 - "@opentelemetry/otlp-transformer": 0.50.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/otlp-exporter-base": 0.52.1 + "@opentelemetry/otlp-transformer": 0.52.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 9666686d85a0966373e5b01e55f6ae9bbe397a27efce4f2bd1eb861e993f5894724b5b5131c4a74061f6bb60b18ba946f27e38850fcd33204ca99f631317139e + checksum: 1d46eb5055925fb1f4383bb65ccec7addcf53ab88f2f5e7393e4e99e72d4fe48a1d45027736d534aeb76e37c217f5a4f9deda51351afa614340196a28db55dab languageName: node linkType: hard -"@opentelemetry/exporter-zipkin@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/exporter-zipkin@npm:1.23.0" +"@opentelemetry/exporter-zipkin@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/exporter-zipkin@npm:1.25.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 08d5f7a9e2af1ad749be8cb6e65d1b312d1e86dd9ec484156ddcd2c0ad3ff27dded459c599ec406d9bccb937e1fd5b58e9af6ff2e5efc3d2e4e83ca2af12920e + checksum: 3ceade67522724642115e008e355b149b606088345c703b39ae4d7a2db3e4d883e9aa22181b49d50444bf0847ff7e0112b3ec4e5feb6acb1411038b3a3e81222 languageName: node linkType: hard -"@opentelemetry/instrumentation-amqplib@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-amqplib@npm:0.36.0" +"@opentelemetry/instrumentation-amqplib@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-amqplib@npm:0.41.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 4035663b7cd569eeabe64548823b14792acde0711a3055435b52cb3b6c7e09a1d8c8b184fecf642bce437ec843d20831e00135d112b6abfc5770c261dc355c81 + checksum: aebf6727120935dd9f05e9d71dd6b2376ee8cdb56e2c8dd7c0fc6820ec1eb998cfa3e5bbb50201dc4e1c1257df293e08b8dc009888f7c55e651f0dfe33a16ede languageName: node linkType: hard @@ -14732,57 +14681,57 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-aws-lambda@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.40.0" +"@opentelemetry/instrumentation-aws-lambda@npm:^0.43.0": + version: 0.43.0 + resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.43.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 "@opentelemetry/propagator-aws-xray": ^1.3.1 "@opentelemetry/resources": ^1.8.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/semantic-conventions": ^1.22.0 "@types/aws-lambda": 8.10.122 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d44b9b19929636fcf28511bb0296c3bb2affeee8cb400dc98d92fa096f332529f7f61d33291d1d6272af16dc8f4a58b4390503728d278111ef113b27e12e8c76 + checksum: f2e7c8ba4a1f5a99f47071d879ac7a9180434a00c7e8db776a803e2a12e90299ad8d1ad3062626f5e8c58dd02232adc07b4a0736a26175b85a951efab5064def languageName: node linkType: hard -"@opentelemetry/instrumentation-aws-sdk@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.40.0" +"@opentelemetry/instrumentation-aws-sdk@npm:^0.43.1": + version: 0.43.1 + resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.43.1" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/propagation-utils": ^0.30.8 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/propagation-utils": ^0.30.10 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 9a112c5d40195c31f5a8b0a646f70a7833e16694db49fd2e4eec05df031075b08c6da804470d0a6a8366c015d997681af5f4af2b07af2c03548866859f6057fd + checksum: b3c5a3893123cfff6f820be65e515d3d42cb7895a98d5c50b8d072e182dc8b8f475936df78a4180d4fa39b5d8e6e7a84b52952842bf14e7708f2c2d5e415492b languageName: node linkType: hard -"@opentelemetry/instrumentation-bunyan@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-bunyan@npm:0.37.0" +"@opentelemetry/instrumentation-bunyan@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-bunyan@npm:0.40.0" dependencies: - "@opentelemetry/api-logs": ^0.50.0 - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/api-logs": ^0.52.0 + "@opentelemetry/instrumentation": ^0.52.0 "@types/bunyan": 1.8.9 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 3cc88d2f8304e7f1456766a16059d21aebcfd65a26e219c3b2145be39e1939813b80d11fff8329f332f3cdd1120167a7c7b39ac503394e104215c84e10bb2b80 + checksum: 67c76e0c39305f069b6564f4fe8315a2b91cdedadea66e2506a9cbeaf04de2c6d86fce75fcf982e556be0fa9e4f84baf2c59b02a5870590ecbf3838d41f8b1d5 languageName: node linkType: hard -"@opentelemetry/instrumentation-cassandra-driver@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.37.0" +"@opentelemetry/instrumentation-cassandra-driver@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 5e6ccd4b7edc718a5c9bf7a0498863e97977c9766a587453a0f04c59a11872ac3d2f33c35a94a85b693f26c381b11c29f426565ddcb6f73be59a90dc0d5b6971 + checksum: 6979a3c0c28226ace0071880d05cf0a418c119b3cf8fcea2e0398288dc7d541ac98935d1a6f9a77e80e8ea9f186e5044b9f791cd5668e3ed33406db6c0a9895f languageName: node linkType: hard @@ -14800,29 +14749,29 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-connect@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-connect@npm:0.35.0" +"@opentelemetry/instrumentation-connect@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/instrumentation-connect@npm:0.38.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 "@types/connect": 3.4.36 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: ebbaa10fb568d94539132bd1a6c821a3228ad5cd83c22482873fba6907192ecb9f712e7c082fc928302e2a01c7be5357a7535b7692b12a6a4fef48541a560054 + checksum: 4dff447ff9a7ee2ca94872d904df260213e8e05b27742713f8dce35593ec8f52bcb07adfb7730e7b208b8b49566e032cdee316f0ccea405e42ffc5804d222750 languageName: node linkType: hard -"@opentelemetry/instrumentation-cucumber@npm:^0.5.0": - version: 0.5.0 - resolution: "@opentelemetry/instrumentation-cucumber@npm:0.5.0" +"@opentelemetry/instrumentation-cucumber@npm:^0.8.0": + version: 0.8.0 + resolution: "@opentelemetry/instrumentation-cucumber@npm:0.8.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 9cd7ecda468b252c270078308ab3c3f2be374fdab963f8cde2c8c6b90666fd7640230aa4caab7e3d0cc1a7ea484aaa81df404d3d1c60b49213b58df3f74c8154 + checksum: 951b9a1d60dfb33a3b4bdcd381e83fd89c2e820f49f86ff1317bcd1127345ec945702760cbbfdc06af75b67e17d85ab9a5f859d90f45d244e17503d0d1e3363d languageName: node linkType: hard @@ -14837,42 +14786,26 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-dataloader@npm:^0.8.0": - version: 0.8.0 - resolution: "@opentelemetry/instrumentation-dataloader@npm:0.8.0" +"@opentelemetry/instrumentation-dataloader@npm:^0.11.0": + version: 0.11.0 + resolution: "@opentelemetry/instrumentation-dataloader@npm:0.11.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: b01916207f36fc00783f39d11a14da4a5a4016efc3f7c356f97b7214bfa88c2bdf1e53db2046b75b1bfb08d6a281cc4d31d1e097d10200389ddb73990355df3b + checksum: 9aa330d08cd6219ee927ab72db3d2d4d080831251bf5dde49eb6c7a1852ac7ea34b8df88d56c19e2638333b58527a90e7c459c9a1529008b74bd489385119ce1 languageName: node linkType: hard -"@opentelemetry/instrumentation-dns@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-dns@npm:0.35.0" +"@opentelemetry/instrumentation-dns@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/instrumentation-dns@npm:0.38.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 semver: ^7.5.4 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 13188d1bc24b7db80d37915c011f8e304fe1ad24348c4e7eab2698cf3e565c67f6eeb34bb0a8fec6c70fd7c06080a6270a190e0968b79e2745406f8b700ff9f9 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-document-load@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-document-load@npm:0.37.0" - dependencies: - "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/sdk-trace-base": ^1.0.0 - "@opentelemetry/sdk-trace-web": ^1.15.0 - "@opentelemetry/semantic-conventions": ^1.22.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 29ef18bd8c1cb1a3f656699fa380c0d9152a2d7f12d2d7d171708abde5bfeb8ddbb01b0054a520a9fe9ca69fc547cb955fc4403ececc8b2f532f688a6269c388 + checksum: e2105d6c8211525573657052dc1d64859046ab259de402b39ee901e8bcab2eafd16f6590e667f86e59db105aea7dab2a4130e2fba9c60e4588fa80cd52427360 languageName: node linkType: hard @@ -14889,16 +14822,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-express@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-express@npm:0.37.0" +"@opentelemetry/instrumentation-express@npm:^0.41.1": + version: 0.41.1 + resolution: "@opentelemetry/instrumentation-express@npm:0.41.1" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 048021e4a9ea063e3c477d17555aeb6133ca5b69addd92c83a27d5297112cb1f0601db44aed93e96e3fb893ce30afd73b9bcfc704741bfa72738af0226ab9426 + checksum: fe2939ab377cc4b1c6dfe77cae6d23156ccd227cc9984bf03eb5f108fa048815d6dd0be9a8240bb3bd053a271789c9928a9e544725e2e099709e9b2c60d456f0 languageName: node linkType: hard @@ -14915,30 +14848,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-fastify@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-fastify@npm:0.35.0" +"@opentelemetry/instrumentation-fastify@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/instrumentation-fastify@npm:0.38.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 6f9699888aff896ceb26966d2b2288a23d069330fe788baf4cc89fc75cfeaccd99ceebf24ce07075af53ee475f93cb83572f0f82cd31a1c8a634e1d8266fd728 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-fetch@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation-fetch@npm:0.50.0" - dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/instrumentation": 0.50.0 - "@opentelemetry/sdk-trace-web": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 70e06fd02ea7c36de55aac9bc526d510f7b3b1da16cab58044ea346eb5bb8c6e5e934ab4de768ad6a0ff7366f3fc1db83f3446453adaad63754e7b5ce6863eb6 + checksum: 3cd7996398d783397782fc327a336e37b1eaabee6c9d8a25daa15d86cf6634408bc790e67207548dcae61f064a4a85780d9ae51bc31414bd61e0291d22a8d3f8 languageName: node linkType: hard @@ -14954,16 +14873,15 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-fs@npm:^0.11.0": - version: 0.11.0 - resolution: "@opentelemetry/instrumentation-fs@npm:0.11.0" +"@opentelemetry/instrumentation-fs@npm:^0.14.0": + version: 0.14.0 + resolution: "@opentelemetry/instrumentation-fs@npm:0.14.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: f540584c75d5b20d3339e96b72117c13cf8d987678285410062b700fc3375e768b3c50057599947e944ac8bacbbcba3c81d5d3921a2411aaeedc47256fc960b5 + checksum: cc6b90e9496bddbbc992adf3cb585b728260e6b22d35e48720e06505e2477e962bd1b62d9d1bf331c877c04ce7b393d87d2f61a3ade08519b73252a342351bf0 languageName: node linkType: hard @@ -14978,15 +14896,14 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-generic-pool@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.35.0" +"@opentelemetry/instrumentation-generic-pool@npm:^0.38.1": + version: 0.38.1 + resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.38.1" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: ce26f2157b36f0232c5d3a61f9800bbac96b10a28602f67cb81650d792565649d23e16a62e37cf9b87d8a8c8054d7b88158ba0c38870a4a7dbce37d5e880f320 + checksum: 19183c1d160d4c54d684305f6638163b5d2b84112b69e8281cd507e5ec5fa505431bc9a435f6c537bdea66a2820b4a231ed64c19da847d76e795036fcbfbd393 languageName: node linkType: hard @@ -15001,26 +14918,26 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-graphql@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-graphql@npm:0.39.0" +"@opentelemetry/instrumentation-graphql@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-graphql@npm:0.42.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 0d731dfe020a7181ff4c182764d80ab346bc80a6d55b271d6f2e5979e7bb1bc4abbb48ff41d54154a49555376d31026a05fd70b37373ace4acbc66f0d4c4f8cb + checksum: cae523cf75312457dc176a1ae2e4157b41025c5d01d8174467c8a62f8031c3a90151888fcdd804778183f58618ec320a24fe78f5bb0a1a72a119dc37245be676 languageName: node linkType: hard -"@opentelemetry/instrumentation-grpc@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation-grpc@npm:0.50.0" +"@opentelemetry/instrumentation-grpc@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation-grpc@npm:0.52.1" dependencies: - "@opentelemetry/instrumentation": 0.50.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/instrumentation": 0.52.1 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d19f7678b8941494ba8c72f4c5d8bece91ca0f13ffd0477fc1345d5da2669a3c2962549378b5bf61f218d3bcaab2a55b97bf765c664d61fc8a29fd516a871855 + checksum: aa14515e5cb8b922f3414540fdaf986bb68f283ab05c57d5f6dad6f9a1f44c6416e5d8e4f6d2467829fb3f4afaf38c5d1c107574481297052477a34e28892cd1 languageName: node linkType: hard @@ -15037,17 +14954,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-hapi@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-hapi@npm:0.36.0" +"@opentelemetry/instrumentation-hapi@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-hapi@npm:0.40.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 - "@types/hapi__hapi": 20.0.13 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 36e301c2fb043a49559ccce1ddeb10063b0befe33d287251e7dae691e265da9527bb5714f8abcf48e3f4e281da93646c17dc7813e582bea002e34091cca71781 + checksum: 71ff845ca2376b5f1cf7514d649f829044dec6486e784351a26278afc092a1cd90eb0eaf34ab79afcae9244e8b126d6059ac0c7c286743ceb8e12126cf7e0255 languageName: node linkType: hard @@ -15065,17 +14981,17 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-http@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation-http@npm:0.50.0" +"@opentelemetry/instrumentation-http@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation-http@npm:0.52.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/instrumentation": 0.50.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/instrumentation": 0.52.1 + "@opentelemetry/semantic-conventions": 1.25.1 semver: ^7.5.2 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 54e48c8f03d82bf6dd5eacac40670833d11053e2c076b426e56908dca8fa9613a14983038d7ec8d9d8546737f25b4fde40d4d92134695cb5924d2497f6bc87a9 + checksum: 67a31e14d9ee4da745b7529777c1525f717b2ddf09f2a9247acaa9677f32e4a863d8f0712c95917fdbe74d8211cf650065bb3a95ef2fdd9f81630e063cbc2d4f languageName: node linkType: hard @@ -15092,17 +15008,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-ioredis@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-ioredis@npm:0.39.0" +"@opentelemetry/instrumentation-ioredis@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-ioredis@npm:0.42.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/redis-common": ^0.36.1 - "@opentelemetry/semantic-conventions": ^1.0.0 - "@types/ioredis4": "npm:@types/ioredis@^4.28.10" + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/redis-common": ^0.36.2 + "@opentelemetry/semantic-conventions": ^1.23.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: f03d6db63bb1b826c9fd0137a72ca7b243bd509ce12fd602ea72a58be8605054c666eddfaed3622d416f316b4e4a0e040de44e079864dd06bb68f9aca03e81ee + checksum: ae4804732b4380007b09ba11051aae927da0c53cd74d556916a838fb4de4f3c2aa7156bc05c4f34e6669411835604a4587822b2b585c51042e502d0e5079c143 languageName: node linkType: hard @@ -15118,15 +15033,27 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-knex@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-knex@npm:0.35.0" +"@opentelemetry/instrumentation-kafkajs@npm:^0.2.0": + version: 0.2.0 + resolution: "@opentelemetry/instrumentation-kafkajs@npm:0.2.0" + dependencies: + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.24.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: f5790d426d87d35159954eed4368b52818f9faf5a999ade66fffc1662c12234caa79a04493266ea2995205e94051b173a831012e3a57c5a9a2417bd07a0e1378 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-knex@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-knex@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: e4a8359ac6685633794cd9f248ea57d1843e92e5f97dad8c7da7717caabf35ecfc3d7385d512a2edab45d19e674f79cb38ef0ec017bc99a1865230169229e404 + checksum: fd44998d57fd6403fcb90fb465cdf071da3d8cbdd161470277ffcb7a7553b04222ceb8d95285391cdb6d5abdd09d90c6fe5d6740a2248c004f163386f10fc235 languageName: node linkType: hard @@ -15143,18 +15070,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-koa@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-koa@npm:0.39.0" +"@opentelemetry/instrumentation-koa@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-koa@npm:0.42.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 "@opentelemetry/semantic-conventions": ^1.22.0 - "@types/koa": 2.14.0 - "@types/koa__router": 12.0.3 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: ed933440dece7dae5e0cfb29a260110cd331c95826a74dd20f11d5c2de1b52c0d6fe8e483b7afee542ccd5ba45d3dcf1302ff69ee73cbd176dbbe5690fbfdf2d + checksum: 4e41f4b1c2abb4d31151ae3b5999af3247affa99fadcf87ff542a8523f108275d83a54057d79453ff56bc40938a622803562db87c826bfac9f99fb409207c6fd languageName: node linkType: hard @@ -15169,27 +15094,27 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-lru-memoizer@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.36.0" +"@opentelemetry/instrumentation-lru-memoizer@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 714406ca211afdfbf6ab493552fe38a1919c6eb7c247dc1a60c6a69c4ef7aadb4f38052cdaea2c9bc5b1403969608769c9de0189db3a91f8a6ce2f8525dfe375 + checksum: 4a9b973bbf4fe77b11fafb4215bd9985f6a3c7ce487af965d98e5502e91a3fb78fc70a3f2238ef46a9c065e1555dfc08c22c39f0d1eb3925cd88c3201c8a79e5 languageName: node linkType: hard -"@opentelemetry/instrumentation-memcached@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-memcached@npm:0.35.0" +"@opentelemetry/instrumentation-memcached@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/instrumentation-memcached@npm:0.38.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.23.0 "@types/memcached": ^2.2.6 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 40f4669fe4c9bad20d59fe9a62063f2bc8b54369303789c768fb6d898214c2c2a7667cb9b653c0825e861c5155a53ecc15fe6a73c6ec24311accc133613b02d6 + checksum: 05e8e25c809461e8aedaf3ba37eec1bbed6cec1307a28a93a986ea1828b791dc2488cd2f83b0fec678c402b7e703fc7ab884274036d0b5df48c8e70b9376648b languageName: node linkType: hard @@ -15206,16 +15131,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-mongodb@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-mongodb@npm:0.42.0" +"@opentelemetry/instrumentation-mongodb@npm:^0.46.0": + version: 0.46.0 + resolution: "@opentelemetry/instrumentation-mongodb@npm:0.46.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 "@opentelemetry/sdk-metrics": ^1.9.1 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: f87c6ce9dd05af6e9c2e86b117d79a908f883c977f9de1d0f9000e5945765e686cedad9b0ab6775c1fcb846cb51cdac0e88b617efdf1ade89ad5fdc2d3af44c0 + checksum: fab5db536e2e95177a0ae0975bd8b9fc76f2d59bf89a6023547ba39071b97789859e1ac0582f86498a263611403a70b5f543b025207180770c07e178b04af5ad languageName: node linkType: hard @@ -15232,16 +15157,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-mongoose@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-mongoose@npm:0.37.0" +"@opentelemetry/instrumentation-mongoose@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-mongoose@npm:0.41.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 50c354438268854585a0d0b8333dbdbdb9ac00d08106d50ccd214e9a478784587075cec1a1be251c0b9ae33663bcab92d54ff4f438c01e11b1a38e8879e81a4d + checksum: 0961538ef1a3d9798e1f70baf206f22318b9a463b2ee50b0270faca434ef974a2bac2d872bebe698cbace17594a27dee43893e11d5ede3704055463fdd781271 languageName: node linkType: hard @@ -15258,16 +15183,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-mysql2@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-mysql2@npm:0.37.0" +"@opentelemetry/instrumentation-mysql2@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-mysql2@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 - "@opentelemetry/sql-common": ^0.40.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/sql-common": ^0.40.1 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d7abe0ccc1d2c5711c4f11077b794c7e07a94fd5866816c8d32e56fee79f4d3e867c19b43b48cb4e4c87cc3d167eb7cd1daf698c2f6e00a8b6ff75c74b6e200d + checksum: acdb9883d1ab92d0bfbe507736888cc0b0d7b25c3d24c27b51add17abceff4bf15988a3cd2e54b93256f28bbc65aa172f116df9277545143e20e8d7b497e2e63 languageName: node linkType: hard @@ -15284,16 +15209,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-mysql@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-mysql@npm:0.37.0" +"@opentelemetry/instrumentation-mysql@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-mysql@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 "@types/mysql": 2.15.22 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: ff70e3fc7f54df420d18709df9c10062031d6fe8ad3affc8f02c13bc6897b2227a5ebb04cc60441799fe3113ecd8bedfc332f86d8f1c4dce432653fef90970ef + checksum: 6eeba06f7507ab9086ef18b9e37802f12ac0a9f6182d4aba3aaf71a3e0f0759cb51c044247653eb8e359383d922a12a0fc0f129b2982bae17910462450cdb189 languageName: node linkType: hard @@ -15309,27 +15234,27 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-nestjs-core@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.36.0" +"@opentelemetry/instrumentation-nestjs-core@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.23.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 9a2b1cd80b6fb4e15ac09d94d7b87bf626857648f221dc47086c411f10af3ad38eecabbfd31f1dfefc0b61c85fae223b539c101e29db236150ccb8679cdc86fe + checksum: 4394754ce3ef4747e39a59a602c744112f9d6d3ee7ef43a51f1562a977a8e2bb2944687d3dd199f0a5941517e71a84ff9ea9ce183d94b63a3311e9de6ac8e09c languageName: node linkType: hard -"@opentelemetry/instrumentation-net@npm:^0.35.0": - version: 0.35.0 - resolution: "@opentelemetry/instrumentation-net@npm:0.35.0" +"@opentelemetry/instrumentation-net@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/instrumentation-net@npm:0.38.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.23.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 58d0a512c79b1dfbe48624ffcbaf5ea07e56256fc13c8cfcf67a98d82c2b5fc5770a9f46e0b1f21ecdb05d3a12342c8b6b6653a7eb07628de0536014ec5b176c + checksum: 13b4e12272ec3c7c2930a4dfd5eabf843d9e008a651c6a5f778236676526a76c563af7d313bf0edb390377d62112c07382b176f64beeffd534ed4f2c04923ccb languageName: node linkType: hard @@ -15348,29 +15273,31 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-pg@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-pg@npm:0.40.0" +"@opentelemetry/instrumentation-pg@npm:^0.43.0": + version: 0.43.0 + resolution: "@opentelemetry/instrumentation-pg@npm:0.43.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/instrumentation": ^0.52.0 "@opentelemetry/semantic-conventions": ^1.22.0 - "@opentelemetry/sql-common": ^0.40.0 + "@opentelemetry/sql-common": ^0.40.1 "@types/pg": 8.6.1 "@types/pg-pool": 2.0.4 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 88a02fadcbd142f5b7eac6e5a035eefcf26709ca30e6c0f7e3a76bb8692e07d9365d41e35a4302ca8bad9202d1369b489e3eaaea2ce069d197d4cda0cd8d68a3 + checksum: d255806b9e5ffd449b7ab3c7c072e919ad8a18118cdf5aec636d89a89d9ac63ef282f280d3b2dd7fa9c57e748fc0c9b38294911bf1aaff35e821079aaeacbd82 languageName: node linkType: hard -"@opentelemetry/instrumentation-pino@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-pino@npm:0.37.0" +"@opentelemetry/instrumentation-pino@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-pino@npm:0.41.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/api-logs": ^0.52.0 + "@opentelemetry/core": ^1.25.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 0c2137cf5c14d370ecbed24590a344f055b52ee5bf4e359f48f24e0bc68922ce91fa8c7afa016f52e897a4e5f108a3d86a6db59a549028088de4f69a5986ef08 + checksum: 1eb2a67513952e3cd7db0ea5fac39a6dffdf28f8ce514af7f39d70263780d1495773e6554bf386e6935c2f0145ca4a73cb4829f6ee91c630bbc85d5ccd164ce0 languageName: node linkType: hard @@ -15387,79 +15314,79 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-redis-4@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-redis-4@npm:0.38.0" +"@opentelemetry/instrumentation-redis-4@npm:^0.41.1": + version: 0.41.1 + resolution: "@opentelemetry/instrumentation-redis-4@npm:0.41.1" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/redis-common": ^0.36.1 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/redis-common": ^0.36.2 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 1c0f85082e72948fa2024d75a48b952f41a5fd505224ad45dd5df69d08fb46b234e969fabd728ccbb7a93601411bb71a1c071d84e50b71603800e99b82618b7a + checksum: d67c542e9ea30bf7dcfb651eba033636d8ee379a135455c314688327c6bcb6512e7f41b7e1e97cbd62475f4afda2582f9be5ac0fd87cf2e008cdfa337a5b667d languageName: node linkType: hard -"@opentelemetry/instrumentation-redis@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-redis@npm:0.38.0" +"@opentelemetry/instrumentation-redis@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-redis@npm:0.41.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/redis-common": ^0.36.1 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/redis-common": ^0.36.2 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d3715e2e0b8de1a004f2cadc5db46d485f8ad0ecdcc4f43a1be630430b72b69517fec5d5f46e9102dde2005e50b22b77d07a27bc9b340ef00ff0afeb6bac5205 + checksum: a1caf71394ece352da20159e007d938fb22cea6c0c266addfa5e59dcf639c7d16404531edb1a7c94a3464db275927f8886f13e67eacedc3e94d97ff4b55c23aa languageName: node linkType: hard -"@opentelemetry/instrumentation-restify@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-restify@npm:0.37.0" +"@opentelemetry/instrumentation-restify@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-restify@npm:0.40.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 8a7abf11e2cf2e532e2059459c2a6ab388f5e82355eaadafc454e7fe006e240184d6c7632d8631e26374928248a45c3648be509e59bd9a474c0e93bc1a43edf4 + checksum: 813b755bd4510d8eef5322e1c9d0aa93407eea62a9bf4f3a82fa2ba38ecfa7e9fe287299eeef84cd4a2912ae9aceecb6392b40fb763d4329f953cb86a69eae61 languageName: node linkType: hard -"@opentelemetry/instrumentation-router@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-router@npm:0.36.0" +"@opentelemetry/instrumentation-router@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-router@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: b6306dbe9386fe0d24e5766d3b3e31d112cd3d2471b42add32725e57d47979afb3615ef9be43120f0fbf1928bd4a64bd89e091df5eea63f6875c134fcd3ea502 + checksum: 802d4179caab4a0ae40745f200809efe6ed1a0e837483dacddb8dcecfe808f7ca42ddac9358715c8949bd59e91da2e7ef1e2afbd0203e22a48af1620f9437c10 languageName: node linkType: hard -"@opentelemetry/instrumentation-socket.io@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-socket.io@npm:0.38.0" +"@opentelemetry/instrumentation-socket.io@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-socket.io@npm:0.41.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 58a7da43e5f6253aec62997a046a1b0cec0b2e459ed6f510d74c10502510f36246333d7cb32500e4f17a42cc160d122082ae6d1296ce8ab762b4ac4e06a267b3 + checksum: a50e66dabc1670906dba51bbcbabcdfdb865157b38e7086ce07275b8d3bde3263fa527a1b054ce55d8bbf8bba3ad6aba64319019015eb85299d46f1e3334f8e0 languageName: node linkType: hard -"@opentelemetry/instrumentation-tedious@npm:^0.9.0": - version: 0.9.0 - resolution: "@opentelemetry/instrumentation-tedious@npm:0.9.0" +"@opentelemetry/instrumentation-tedious@npm:^0.13.0": + version: 0.13.0 + resolution: "@opentelemetry/instrumentation-tedious@npm:0.13.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/semantic-conventions": ^1.0.0 - "@types/tedious": ^4.0.10 + "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/semantic-conventions": ^1.22.0 + "@types/tedious": ^4.0.14 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 24da18f68ceedc239abd50438ae0a360c320512d15c6875438ee048bea8b336e75dd4aa97404131afcb5b820937f778a739961bf060c4d05dcecd05bcf4be3f5 + checksum: 71833d6989d91a4696c3c73836fc12f31930f9fe30e95d20107d5e54054d1276baeeda7ee125c4f43d9859a1337fe017d1d91899e28d506ab275a68ab84e3bee languageName: node linkType: hard @@ -15475,58 +15402,43 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-user-interaction@npm:^0.37.0": - version: 0.37.0 - resolution: "@opentelemetry/instrumentation-user-interaction@npm:0.37.0" +"@opentelemetry/instrumentation-undici@npm:^0.5.0": + version: 0.5.0 + resolution: "@opentelemetry/instrumentation-undici@npm:0.5.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.50.0 - "@opentelemetry/sdk-trace-web": ^1.8.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: - "@opentelemetry/api": ^1.3.0 - zone.js: ^0.11.4 || ^0.13.0 || ^0.14.0 - checksum: c086d56da522fd03fc462279ea0782a99d44b76e11f5edde6e62290b27de0cba24ac27cff3b4b43f93d1c809a4346c0a0b52ff606acc5a2ed8dd83eaebaf20af + "@opentelemetry/api": ^1.7.0 + checksum: 534a5f9171fcd806332096a996fe1a3ceffaef5e04c3332b82d73242909b21941c4b857e6658d75a29c140c4f5c89d39a157bc7023a774d61b559d2086137138 languageName: node linkType: hard -"@opentelemetry/instrumentation-winston@npm:^0.36.0": - version: 0.36.0 - resolution: "@opentelemetry/instrumentation-winston@npm:0.36.0" +"@opentelemetry/instrumentation-winston@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-winston@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.50.0 + "@opentelemetry/api-logs": ^0.52.0 + "@opentelemetry/instrumentation": ^0.52.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 9ffd1142071f307c324c30ceddfd2a632e6f5f79fc5ee91f110ea0ee3e07372ec7ddd3e959e6cdd0292ab267e07775459d1342d2c050ef4d1c1416999a78647c + checksum: df2d28ae4dbe65294afeaee36b5b914d96473674e968f939bfd64a2288730b21911c920168b4f2e4961d8b03c988e327ae828b84a5d1875cb424b84b6fd80e1a languageName: node linkType: hard -"@opentelemetry/instrumentation-xml-http-request@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation-xml-http-request@npm:0.50.0" - dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/instrumentation": 0.50.0 - "@opentelemetry/sdk-trace-web": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: c1210f72d6a205f85cf38928159dad42a3726db83f0ad139b3ec2054d1ef0358e1a93bc75c260e9d9b71d12b12ce943b6f6db0608dfa2756cf142045472db670 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation@npm:0.50.0, @opentelemetry/instrumentation@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation@npm:0.50.0" +"@opentelemetry/instrumentation@npm:0.52.1, @opentelemetry/instrumentation@npm:^0.49 || ^0.50 || ^0.51 || ^0.52.0, @opentelemetry/instrumentation@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/instrumentation@npm:0.52.1" dependencies: - "@opentelemetry/api-logs": 0.50.0 + "@opentelemetry/api-logs": 0.52.1 "@types/shimmer": ^1.0.2 - import-in-the-middle: 1.7.1 + import-in-the-middle: ^1.8.1 require-in-the-middle: ^7.1.1 semver: ^7.5.2 shimmer: ^1.2.1 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 371398639ca68c188d4b77a0034ea369222a2a1de421be37190900bade1210802a50b53cc48fd21206917817319a92a4cb52bd92bd534889355b54316145e634 + checksum: e8b4f202dc9355ca46714349a5e1663346e162f79706eed38015edf38fc536330fde4cc19ed7d3d6b03258c890c1dc0ba6d658d7aac3f41f1803bd03699d2701 languageName: node linkType: hard @@ -15546,122 +15458,110 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation@npm:^0.49 || ^0.50 || ^0.51 || ^0.52.0": +"@opentelemetry/otlp-exporter-base@npm:0.52.1": version: 0.52.1 - resolution: "@opentelemetry/instrumentation@npm:0.52.1" + resolution: "@opentelemetry/otlp-exporter-base@npm:0.52.1" dependencies: - "@opentelemetry/api-logs": 0.52.1 - "@types/shimmer": ^1.0.2 - import-in-the-middle: ^1.8.1 - require-in-the-middle: ^7.1.1 - semver: ^7.5.2 - shimmer: ^1.2.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/otlp-transformer": 0.52.1 peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: e8b4f202dc9355ca46714349a5e1663346e162f79706eed38015edf38fc536330fde4cc19ed7d3d6b03258c890c1dc0ba6d658d7aac3f41f1803bd03699d2701 + "@opentelemetry/api": ^1.0.0 + checksum: a1629886907a393e24a91ef5adc389e093d3725dd2957bc29903442541b2a30673100b13c200c37fe0a2e2fec53ced8b47ad90064ebc49f2e9382c2b1719d3c3 languageName: node linkType: hard -"@opentelemetry/otlp-exporter-base@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/otlp-exporter-base@npm:0.50.0" +"@opentelemetry/otlp-grpc-exporter-base@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.52.1" dependencies: - "@opentelemetry/core": 1.23.0 + "@grpc/grpc-js": ^1.7.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/otlp-exporter-base": 0.52.1 + "@opentelemetry/otlp-transformer": 0.52.1 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: e1e6586a64d753e542f28858c1618776deeaf639afd50e4ff325c14f7571ac91546928d0f2f89b2287e7cdbadf3211e3f9dd844139f64e6253e99f71b6cc1f07 + checksum: 22b1eb92ceec038807d60b7341bea7237ea903f0e2cf91b4fefbaf4cc283518b89a819da18b966a4b61915c7143e00ccc51ef909c3de5fa518b0dbe6c78abc5f languageName: node linkType: hard -"@opentelemetry/otlp-grpc-exporter-base@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.50.0" +"@opentelemetry/otlp-transformer@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/otlp-transformer@npm:0.52.1" dependencies: - "@grpc/grpc-js": ^1.7.1 - "@opentelemetry/core": 1.23.0 - "@opentelemetry/otlp-exporter-base": 0.50.0 - protobufjs: ^7.2.3 + "@opentelemetry/api-logs": 0.52.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-logs": 0.52.1 + "@opentelemetry/sdk-metrics": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 + protobufjs: ^7.3.0 peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 97a4e69d2834c840f1f037737eb378c309e5645002490b5bc51b836303a205a65658e2b1ec512dd613207626517efbe744b46225a9b3079b782cdda0a168fcf6 + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 0e083ee484a79506d8ce41a8dd5d2d4d7669c380ac1ef9c313c348a6af8384365d2b90d99165590cced6453ce9192c0fe270d9cc974e9240e8ca02a6cf70151d languageName: node linkType: hard -"@opentelemetry/otlp-proto-exporter-base@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/otlp-proto-exporter-base@npm:0.50.0" - dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/otlp-exporter-base": 0.50.0 - protobufjs: ^7.2.3 +"@opentelemetry/propagation-utils@npm:^0.30.10": + version: 0.30.11 + resolution: "@opentelemetry/propagation-utils@npm:0.30.11" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: d39f61a5ca31ac9cbb6ac7e77c71afebc7b40bf247361c99e34f8c00aeae88db1d4c7e6742f9dd01d2334ee50c7db8e24d7d9df67a65ee005c947358611f2050 + checksum: 4cce9958ca40b042adda8601b651799f1a140a513dd8c085094cb88265470bf40a04ed5f84301d891fef90f59698802e20828311b6189e4ab6c741101d8ac248 languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/otlp-transformer@npm:0.50.0" +"@opentelemetry/propagator-aws-xray@npm:^1.3.1": + version: 1.26.0 + resolution: "@opentelemetry/propagator-aws-xray@npm:1.26.0" dependencies: - "@opentelemetry/api-logs": 0.50.0 - "@opentelemetry/core": 1.23.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-logs": 0.50.0 - "@opentelemetry/sdk-metrics": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 + "@opentelemetry/core": 1.26.0 peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.9.0" - checksum: d2637146cdb1a3c7c311f03c8d8a11c1c4b57c63ac3532865096055d603a334bb4b5a63cecaba96f27dd0f3b8b4c7ffcebd248d85c47b7e60a5b5e7ae821219c + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 34a0c208c28a558dbdf4650f7aac40bb93ad4e3be6458bdcbf4db1516f0b2bd16bf59fb05ab2ad00fd066e11c5923767def4d3498ad4dc4d7bc54494a1b04719 languageName: node linkType: hard -"@opentelemetry/propagation-utils@npm:^0.30.8": - version: 0.30.8 - resolution: "@opentelemetry/propagation-utils@npm:0.30.8" +"@opentelemetry/propagator-b3@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/propagator-b3@npm:1.25.1" + dependencies: + "@opentelemetry/core": 1.25.1 peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 8171d7ed404d02a43507698cfc835a4e69172399d624e308c248e00c910377cb41c816490cb56f2190ba3bc26f586d8199496f9de8e9595fa37525eb856b08e6 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 7aef1e192b33533dfc9fd759efc9cea65cc29f1f35c0e1a2bb4065244ed9a78b18d4a9c843c646484e61f83834d6162ae2a1ebfc40750e4eae844e5dd4b85565 languageName: node linkType: hard -"@opentelemetry/propagator-aws-xray@npm:^1.3.1": - version: 1.3.1 - resolution: "@opentelemetry/propagator-aws-xray@npm:1.3.1" +"@opentelemetry/propagator-b3@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/propagator-b3@npm:1.27.0" dependencies: - "@opentelemetry/core": ^1.0.0 + "@opentelemetry/core": 1.27.0 peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 9af526b4e181913929cfed12af7ac2248fd1a4189ad50edde9035dd3936c025ef8fc66bc58de21fb99429a4b19994404eee03264dbc2b26a522bb6c9c804a81c + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 4bfe531e1fb5606d25d09e7b3c84188938e76618f8a0a098d57ebacf9f7bafaab24ba6e6e26cbb4c3da3ea8ecd31e2c68e86735ef48f45cdfc2e698c0b098249 languageName: node linkType: hard -"@opentelemetry/propagator-b3@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/propagator-b3@npm:1.23.0" +"@opentelemetry/propagator-jaeger@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/propagator-jaeger@npm:1.25.1" dependencies: - "@opentelemetry/core": 1.23.0 + "@opentelemetry/core": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 8478a4ac3fcad3ea53ed7af70c7da22dee48a262aef2bb2c64a039a3aff1368476b23ce385b95b3c34334a29d7964c98ca3c08bc05dd2999a891faf3ab858799 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 89b1e9f4daa2494ef472a16ddf1eb8cad547f78bf3255cf724113c346fe9be9965fd36ed5ffcb4098ecfec25ebb120d8e3e06a5783999cdf552f164047938028 languageName: node linkType: hard -"@opentelemetry/propagator-jaeger@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/propagator-jaeger@npm:1.23.0" +"@opentelemetry/propagator-jaeger@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/propagator-jaeger@npm:1.27.0" dependencies: - "@opentelemetry/core": 1.23.0 + "@opentelemetry/core": 1.27.0 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 240f27f15473704a5cf8549ea22b1640d865ef40c3da65b023e7f44cc89422b9f983061e4508a3f4576339907625c8f725c42e7f5e77c243c833e03d9490880d - languageName: node - linkType: hard - -"@opentelemetry/redis-common@npm:^0.36.1": - version: 0.36.1 - resolution: "@opentelemetry/redis-common@npm:0.36.1" - checksum: 85a992408ed2057ee3f6932b68553bae6a8be6bffdf6e49244df50252a494a63bfa486ecd203ce8a69f67fa79c5a74f5ae84f425de54727c628bca679b5cd16b + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 87ab8a15b57230e40b9f59e63a01a31d38bfef77cafb91543a1549c179a9d01fef73ce5cf58ad7c6ed0093ab3b4a64ea9ba00e9d257d4084447267fe39f9163d languageName: node linkType: hard @@ -15672,54 +15572,69 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.28.8": - version: 0.28.8 - resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.28.8" +"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.29.0": + version: 0.29.3 + resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.29.3" dependencies: - "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/core": ^1.26.0 + "@opentelemetry/resources": ^1.10.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: dcdd51bc1488617c7c2d6096514bb9e271891741ed75e21712bcb6e37de82045f3d1c2fd897d59b85f78dac2d54e25d65c467b24a6f409ded98f491a160bd167 + checksum: e3cb36aa8b4ce7b6fc60fe15f51139493728f4c8e7af11f70cb567b5667680c16147bc382aa1a330b8bca26b392e7dddd5f66b6c393ca50aa315329baa0bea14 languageName: node linkType: hard -"@opentelemetry/resource-detector-aws@npm:^1.4.1": - version: 1.4.1 - resolution: "@opentelemetry/resource-detector-aws@npm:1.4.1" +"@opentelemetry/resource-detector-aws@npm:^1.6.0": + version: 1.6.2 + resolution: "@opentelemetry/resource-detector-aws@npm:1.6.2" dependencies: "@opentelemetry/core": ^1.0.0 - "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/resources": ^1.10.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 1a3b6a5fc476e34a9e49a6c42858165c13d10e276607c9e55425a94875bded5445fd163b9e1b690a8304e07ade98104bb6d888d2857c17385f16f13ac3b9e6f4 + checksum: 82047c95f4ac1cf9eb6688ac924562cdf5e31bc9ebf4d1f2205e652de92350e6f85b111a220a1156d77fcafdbf513aaabb69919da19c8ce83fc3e6a3dd0dde8e languageName: node linkType: hard -"@opentelemetry/resource-detector-container@npm:^0.3.8": - version: 0.3.8 - resolution: "@opentelemetry/resource-detector-container@npm:0.3.8" +"@opentelemetry/resource-detector-azure@npm:^0.2.10": + version: 0.2.11 + resolution: "@opentelemetry/resource-detector-azure@npm:0.2.11" dependencies: - "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/core": ^1.25.1 + "@opentelemetry/resources": ^1.10.1 + "@opentelemetry/semantic-conventions": ^1.27.0 + peerDependencies: + "@opentelemetry/api": ^1.0.0 + checksum: 13515be1d57f9a9611064224e55766a7a66176d85bcbc62e7313e261c0a40e8c02e6ec024ed51f1e20ce16f3e969a601a756d2130f01b24a1e088743886903b3 + languageName: node + linkType: hard + +"@opentelemetry/resource-detector-container@npm:^0.4.0": + version: 0.4.4 + resolution: "@opentelemetry/resource-detector-container@npm:0.4.4" + dependencies: + "@opentelemetry/core": ^1.26.0 + "@opentelemetry/resources": ^1.10.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 03c4cc004ed9ac5b311ac1139b01620388c324cfb09eef68dd0f89c619f94c2fb9fcccfce4a46db2f4c245f0584dc9a68f1383ccbea79fa5e42c06e3832a2080 + checksum: c99a859718fa496062a4392edba478bae38f24a78450f38df31cb13014ef159e6bb48cb41fba4eef717229f85bc79dc8f4031e9f263efa8eff3c583b5efa8069 languageName: node linkType: hard -"@opentelemetry/resource-detector-gcp@npm:^0.29.8": - version: 0.29.8 - resolution: "@opentelemetry/resource-detector-gcp@npm:0.29.8" +"@opentelemetry/resource-detector-gcp@npm:^0.29.10": + version: 0.29.12 + resolution: "@opentelemetry/resource-detector-gcp@npm:0.29.12" dependencies: "@opentelemetry/core": ^1.0.0 - "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.0.0 + "@opentelemetry/resources": ^1.10.0 + "@opentelemetry/semantic-conventions": ^1.27.0 gcp-metadata: ^6.0.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 00eee74e1ad754923136aba40c820293bf38d3ffbcb30da4f7c43083a3db5a0205af10ba6d9ad237dabc8497cb8a4c1997a31b36b6b5dd694fb47bcb43cc73a4 + checksum: 5460a3c3cf9484dc38aff5625f7f905c08b380468b1ae3e0677c3b5a9319ba2297fd00a517361c28dcd8812a73e680fe5bf123bbcfdd240bc7d786b681998d3d languageName: node linkType: hard @@ -15735,31 +15650,19 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:1.17.0": - version: 1.17.0 - resolution: "@opentelemetry/resources@npm:1.17.0" - dependencies: - "@opentelemetry/core": 1.17.0 - "@opentelemetry/semantic-conventions": 1.17.0 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.7.0" - checksum: 517dba494be0a55ff489b086b8ba33401993d7231483c5e37ff8bc2d360846064ea71cb37b0e7fed39de4f8291a0cccdbd3724e8d9751c72c09ecc66a312f2f4 - languageName: node - linkType: hard - -"@opentelemetry/resources@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/resources@npm:1.23.0" +"@opentelemetry/resources@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/resources@npm:1.25.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: e780d34f8107cdd2853aab3c0c680a817da54d9c6020bba9b8a6b8e7b637487592d87440e5c4f09a10dfad7aededde34532e0e337a5c2d441bf26dd921836cfc + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 806e5aabbc93afcab767dc84707f702ca51bbc93e4565eb69a8591ed2fe78439aca19c5ca0d9f044c85ed97b9efb35936fdb65bef01f5f3e68504002c8a07220 languageName: node linkType: hard -"@opentelemetry/resources@npm:1.26.0, @opentelemetry/resources@npm:^1.26.0": +"@opentelemetry/resources@npm:1.26.0, @opentelemetry/resources@npm:^1.10.0, @opentelemetry/resources@npm:^1.26.0, @opentelemetry/resources@npm:^1.8.0": version: 1.26.0 resolution: "@opentelemetry/resources@npm:1.26.0" dependencies: @@ -15771,53 +15674,41 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:^1.0.0, @opentelemetry/resources@npm:^1.12.0": - version: 1.13.0 - resolution: "@opentelemetry/resources@npm:1.13.0" - dependencies: - "@opentelemetry/core": 1.13.0 - "@opentelemetry/semantic-conventions": 1.13.0 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.5.0" - checksum: ef0a11596f27b5e1c13b74357da06c5c2725a1056df0a583562dbcc739927ad85bb8bdec4e01f4b43f348b448c0146c033b135840a7d388b75cc751a33a6364e - languageName: node - linkType: hard - -"@opentelemetry/resources@npm:^1.8.0": - version: 1.8.0 - resolution: "@opentelemetry/resources@npm:1.8.0" +"@opentelemetry/resources@npm:1.27.0, @opentelemetry/resources@npm:^1.10.1, @opentelemetry/resources@npm:^1.24.0": + version: 1.27.0 + resolution: "@opentelemetry/resources@npm:1.27.0" dependencies: - "@opentelemetry/core": 1.8.0 - "@opentelemetry/semantic-conventions": 1.8.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/semantic-conventions": 1.27.0 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.4.0" - checksum: eeea7864c486d31679dbae3c31e9badf277ece24ba8bd063e2bcc34b2d7240f543678b8106743aff30c6b0f028f2bed286d64cfd7b5cde6f9a0f6a9271a3fce1 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 43d298afea7daf7524e6b98c1441bcce9fa73b76aecf17e36cabb1a4cfaae6818acf9759d3e42706b1fd91243644076d2291e78c3ed81641d3b351fcff6cb9a9 languageName: node linkType: hard -"@opentelemetry/sdk-logs@npm:0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/sdk-logs@npm:0.50.0" +"@opentelemetry/sdk-logs@npm:0.52.1": + version: 0.52.1 + resolution: "@opentelemetry/sdk-logs@npm:0.52.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/resources": 1.23.0 + "@opentelemetry/api-logs": 0.52.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/resources": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.4.0 <1.9.0" - "@opentelemetry/api-logs": ">=0.39.1" - checksum: e93be98f4ea2b64dd0fc0aebc5dfa7276f995a0822cae727455988397cb0c10f7696dbabc4d01a7b09d515faceffa2858c3329a85841ae79c44072e8c0911df8 + "@opentelemetry/api": ">=1.4.0 <1.10.0" + checksum: 16bdccd8250d96df0ffcadb63b107135d207072c1da52d056f00b211ca56924413d53a529cc0e362d96bd1bf33aa801eba51b395c5cb290b8c66fb1b988a7ff6 languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/sdk-metrics@npm:1.23.0" +"@opentelemetry/sdk-metrics@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/sdk-metrics@npm:1.25.1" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/resources": 1.23.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/resources": 1.25.1 lodash.merge: ^4.6.2 peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.9.0" - checksum: ca24338ceb537fa9c4d9a87b4e81164db1275d96ecf97347a2e48f07cfaab0fb25013091898fb435751eb488efcd6d01232150cce3c034263fe293ce6bd83abd + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: efd3902d30e75bfc16e4208ffc92096743148ed8cec84900d05f98cc17ff146c711c398c3a526589433509c82399641b0759b4ba9fffc12be2e5007a55af7517 languageName: node linkType: hard @@ -15834,52 +15725,52 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-node@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/sdk-node@npm:0.50.0" +"@opentelemetry/sdk-node@npm:^0.52.0": + version: 0.52.1 + resolution: "@opentelemetry/sdk-node@npm:0.52.1" dependencies: - "@opentelemetry/api-logs": 0.50.0 - "@opentelemetry/core": 1.23.0 - "@opentelemetry/exporter-trace-otlp-grpc": 0.50.0 - "@opentelemetry/exporter-trace-otlp-http": 0.50.0 - "@opentelemetry/exporter-trace-otlp-proto": 0.50.0 - "@opentelemetry/exporter-zipkin": 1.23.0 - "@opentelemetry/instrumentation": 0.50.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/sdk-logs": 0.50.0 - "@opentelemetry/sdk-metrics": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 - "@opentelemetry/sdk-trace-node": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/api-logs": 0.52.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/exporter-trace-otlp-grpc": 0.52.1 + "@opentelemetry/exporter-trace-otlp-http": 0.52.1 + "@opentelemetry/exporter-trace-otlp-proto": 0.52.1 + "@opentelemetry/exporter-zipkin": 1.25.1 + "@opentelemetry/instrumentation": 0.52.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/sdk-logs": 0.52.1 + "@opentelemetry/sdk-metrics": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 + "@opentelemetry/sdk-trace-node": 1.25.1 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.9.0" - checksum: 1ca47e0cec7832a291e20fc838bfb2a7307b6d041a29b0ace1a5f3eb9f77fa8bad6e40d55eb19e5ab7c153afb866890bfc415e843d6b950691527566a592ad35 + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 601f598fb0c7d50387c4d078a7f0436598c99f3a4c0eda4dd18bae00474d6ee9836adb148fb230b6bc351b8119c5dcba1f9803adfb807d19f30956c0a9a7eba5 languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.17.0, @opentelemetry/sdk-trace-base@npm:^1.0.0": - version: 1.17.0 - resolution: "@opentelemetry/sdk-trace-base@npm:1.17.0" +"@opentelemetry/sdk-trace-base@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/sdk-trace-base@npm:1.25.1" dependencies: - "@opentelemetry/core": 1.17.0 - "@opentelemetry/resources": 1.17.0 - "@opentelemetry/semantic-conventions": 1.17.0 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/resources": 1.25.1 + "@opentelemetry/semantic-conventions": 1.25.1 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.7.0" - checksum: e009969df4edccb6898fd7af2941f9f27c530e195429309a4057ae6cb8080e4cd008fb9437acb361ccf64ca40e2a8747309cb3545916c68587eb45adb012b2db + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 8ac97f7d8d36bf412c5f47ff98ded07c5dfd11602a6ae7657ec7b5f50bb6ddaa20fc682626afcf74e21b375dbad0d1d47c8e20204d5139431afec25165f6252b languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.23.0, @opentelemetry/sdk-trace-base@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/sdk-trace-base@npm:1.23.0" +"@opentelemetry/sdk-trace-base@npm:1.27.0": + version: 1.27.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.27.0" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/resources": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/resources": 1.27.0 + "@opentelemetry/semantic-conventions": 1.27.0 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 564a14a38b151d793949da95949a5eb4e0034ff95356162a7fcf7fe6a81b312cd8d601d6e46b303e6d9f785152ff28621cb7bd114f61e064bfdfa77ed28ca8cc + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: d28c36724aeaf4884f7957e2ab138d9a0ca715a68b2ad23e2935ff0e39cd438c57fd0c8cc85fd5e280464857ede1ae8f9c8e40a37088a1e34d2e625e77276fee languageName: node linkType: hard @@ -15896,52 +15787,48 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-node@npm:1.23.0, @opentelemetry/sdk-trace-node@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/sdk-trace-node@npm:1.23.0" +"@opentelemetry/sdk-trace-node@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/sdk-trace-node@npm:1.25.1" dependencies: - "@opentelemetry/context-async-hooks": 1.23.0 - "@opentelemetry/core": 1.23.0 - "@opentelemetry/propagator-b3": 1.23.0 - "@opentelemetry/propagator-jaeger": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 + "@opentelemetry/context-async-hooks": 1.25.1 + "@opentelemetry/core": 1.25.1 + "@opentelemetry/propagator-b3": 1.25.1 + "@opentelemetry/propagator-jaeger": 1.25.1 + "@opentelemetry/sdk-trace-base": 1.25.1 semver: ^7.5.2 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 165f26d77672d6745e9b1d3af78e3b1afcd4fe1b48e0eaef1aa67e9c86e822f9d0947cb0066c6a1080bdcf03c9da268870cace839254d1fee03446b25dbf6d30 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: f6835329651f5d888a90d95f6f86d5f660158029af2e01b68a5a0e21b2eb9dd40147dd6b0c321a12f65924c3ec1574d4ed2c6f05eea5c85280a82f3a95436bdb languageName: node linkType: hard -"@opentelemetry/sdk-trace-web@npm:1.23.0, @opentelemetry/sdk-trace-web@npm:^1.23.0": - version: 1.23.0 - resolution: "@opentelemetry/sdk-trace-web@npm:1.23.0" +"@opentelemetry/sdk-trace-node@npm:^1.26.0": + version: 1.27.0 + resolution: "@opentelemetry/sdk-trace-node@npm:1.27.0" dependencies: - "@opentelemetry/core": 1.23.0 - "@opentelemetry/sdk-trace-base": 1.23.0 - "@opentelemetry/semantic-conventions": 1.23.0 + "@opentelemetry/context-async-hooks": 1.27.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/propagator-b3": 1.27.0 + "@opentelemetry/propagator-jaeger": 1.27.0 + "@opentelemetry/sdk-trace-base": 1.27.0 + semver: ^7.5.2 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.9.0" - checksum: 493e5d52c3f7a35e52c7634daba5b1174995d1064159f81050adc8434d0867d877f06ffa0617154ea1ca68954e0b0cc313d83836dff95d83cbc5ddb4638e2c11 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 2abbd609ebefc3c8ece87321281d3b31a59201e799e55cbde0ad0d87b7643e4e5ac47d83dcde2306d32780438b6eba49f57113bc56ed02e95b6fab1732780247 languageName: node linkType: hard -"@opentelemetry/sdk-trace-web@npm:^1.15.0, @opentelemetry/sdk-trace-web@npm:^1.8.0": - version: 1.17.0 - resolution: "@opentelemetry/sdk-trace-web@npm:1.17.0" +"@opentelemetry/sdk-trace-web@npm:^1.26.0": + version: 1.27.0 + resolution: "@opentelemetry/sdk-trace-web@npm:1.27.0" dependencies: - "@opentelemetry/core": 1.17.0 - "@opentelemetry/sdk-trace-base": 1.17.0 - "@opentelemetry/semantic-conventions": 1.17.0 + "@opentelemetry/core": 1.27.0 + "@opentelemetry/sdk-trace-base": 1.27.0 + "@opentelemetry/semantic-conventions": 1.27.0 peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.7.0" - checksum: 7fff2df3d08fc7ccff5ff9f4ac801f586450e4e2b310ca0adc88616ba9668aa6cc15279ef0241380ea2fb1798fded376cff0b009443cb1915988c2c88e39fed7 - languageName: node - linkType: hard - -"@opentelemetry/semantic-conventions@npm:1.13.0": - version: 1.13.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.13.0" - checksum: 9cccf1d73315fed3920bb2201c0e82f66e58dddfa475314b6613780c2804570d6f657be3894eb8b84a2a543c9b8cd520587f5d6cd4b62bc6731d7299b4c5ee69 + "@opentelemetry/api": ">=1.0.0 <1.10.0" + checksum: 7c5c354fc9400adef70e6f169edd61a11ebcd36ca15993b366e8f6cfb296f248786be41d327ca971ad4f24b6623a5b8af2fb88fd760af6419c6313a7288ed6f9 languageName: node linkType: hard @@ -15952,34 +15839,20 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.17.0": - version: 1.17.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.17.0" - checksum: 3cb99118b3720aed37fa71d9b6c38847a481d5287653275477d30126de9e548f63a302efbd8a2086a747442880598bbde95ef17f8016dce45b85798696f12be4 - languageName: node - linkType: hard - -"@opentelemetry/semantic-conventions@npm:1.23.0, @opentelemetry/semantic-conventions@npm:^1.22.0": - version: 1.23.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.23.0" - checksum: a4bd6e67e0fe5821be7dc14baff77574e9881d208a63740a3ab416b367c132bc77cf3c0b398daea1344c9af2f32383cf6c7da3141ba6d1e87e30756e4f2234b8 +"@opentelemetry/semantic-conventions@npm:1.25.1": + version: 1.25.1 + resolution: "@opentelemetry/semantic-conventions@npm:1.25.1" + checksum: fea418a4b09c55121c6da11c49dd2105116533838c484aead17e8acf8029dad711e145849812f9c61f9e48fad8e2b6cf103d2c18847ca993032ce9b27c2f863d languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.27.0, @opentelemetry/semantic-conventions@npm:^1.27.0": +"@opentelemetry/semantic-conventions@npm:1.27.0, @opentelemetry/semantic-conventions@npm:^1.23.0, @opentelemetry/semantic-conventions@npm:^1.24.0, @opentelemetry/semantic-conventions@npm:^1.27.0": version: 1.27.0 resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" checksum: 26d85f8d13c8c64024f7a84528cff41d56afc9829e7ff8a654576404f8b2c1a9c264adcc6fa5a9551bacdd938a4a464041fa9493e0a722e5605f2c2ae6752398 languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.7.0": - version: 1.7.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.7.0" - checksum: 5214501648a002cc92fc6ad2296be8fa95b1803b360fc7732222e74a7f3743f3f72952740bed18ea9207741e450f3a32bf11752d732dc83f8770681eb8a6656b - languageName: node - linkType: hard - "@opentelemetry/semantic-conventions@npm:1.8.0": version: 1.8.0 resolution: "@opentelemetry/semantic-conventions@npm:1.8.0" @@ -15987,10 +15860,10 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:^1.0.0": - version: 1.4.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.4.0" - checksum: 9e8ac93f048dd62d1ba93f38bf119720aa4da8d6b00f75fddda56c8c87c944d4d81d18438739c167c0133b25dabaa6ac7a3f4245f8ab4994dc25e7605d1faa0e +"@opentelemetry/semantic-conventions@npm:^1.22.0": + version: 1.23.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.23.0" + checksum: a4bd6e67e0fe5821be7dc14baff77574e9881d208a63740a3ab416b367c132bc77cf3c0b398daea1344c9af2f32383cf6c7da3141ba6d1e87e30756e4f2234b8 languageName: node linkType: hard @@ -16001,17 +15874,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sql-common@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/sql-common@npm:0.40.0" - dependencies: - "@opentelemetry/core": ^1.1.0 - peerDependencies: - "@opentelemetry/api": ^1.1.0 - checksum: 65249c13f160a9f2d2e408bc2c33a90f47d1721b8280c9f087d722fe0d0bfed01289c165b3f668e6c512d4fe2da9a858d25bbe906e667cd12a4aedcbe9da4e1c - languageName: node - linkType: hard - "@opentelemetry/sql-common@npm:^0.40.1": version: 0.40.1 resolution: "@opentelemetry/sql-common@npm:0.40.1" @@ -18131,6 +17993,21 @@ __metadata: languageName: node linkType: hard +"@sentry/nestjs@npm:^8.35.0": + version: 8.35.0 + resolution: "@sentry/nestjs@npm:8.35.0" + dependencies: + "@sentry/core": 8.35.0 + "@sentry/node": 8.35.0 + "@sentry/types": 8.35.0 + "@sentry/utils": 8.35.0 + peerDependencies: + "@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/core": ^8.0.0 || ^9.0.0 || ^10.0.0 + checksum: ba8c27dc546405db045cf660a49988357ae148732626dab69dbe58a41ba2e398b183d4dc6f2f95b39fe8fe0812d7ab1e5dcee5552c3d54f5b71d80588f8d0237 + languageName: node + linkType: hard + "@sentry/nextjs@npm:^8.35.0": version: 8.35.0 resolution: "@sentry/nextjs@npm:8.35.0" @@ -24598,22 +24475,6 @@ __metadata: languageName: node linkType: hard -"@types/hapi__hapi@npm:20.0.13": - version: 20.0.13 - resolution: "@types/hapi__hapi@npm:20.0.13" - dependencies: - "@hapi/boom": ^9.0.0 - "@hapi/iron": ^6.0.0 - "@hapi/podium": ^4.1.3 - "@types/hapi__catbox": "*" - "@types/hapi__mimos": "*" - "@types/hapi__shot": "*" - "@types/node": "*" - joi: ^17.3.0 - checksum: 02d0f91b3b0900e05b6e8d31ae664e20d9c41238155c56fe8c4ca3e8f3d6bc99a8be117eb9aced2141435c855ce8be9bade575ce97af0d5ba8939268ba40d798 - languageName: node - linkType: hard - "@types/hapi__hapi@npm:^20.0.10": version: 20.0.10 resolution: "@types/hapi__hapi@npm:20.0.10" @@ -24738,15 +24599,6 @@ __metadata: languageName: node linkType: hard -"@types/ioredis4@npm:@types/ioredis@^4.28.10": - version: 4.28.10 - resolution: "@types/ioredis@npm:4.28.10" - dependencies: - "@types/node": "*" - checksum: 0f2788cf25f490d3b345db8c5f8b8ce3f6c92cc99abcf744c8f974f02b9b3875233b3d22098614c462a0d6c41c523bd655509418ea88eb6249db6652290ce7cf - languageName: node - linkType: hard - "@types/ioredis@npm:^4.26.4": version: 4.26.4 resolution: "@types/ioredis@npm:4.26.4" @@ -25002,31 +24854,6 @@ __metadata: languageName: node linkType: hard -"@types/koa@npm:2.14.0": - version: 2.14.0 - resolution: "@types/koa@npm:2.14.0" - dependencies: - "@types/accepts": "*" - "@types/content-disposition": "*" - "@types/cookies": "*" - "@types/http-assert": "*" - "@types/http-errors": "*" - "@types/keygrip": "*" - "@types/koa-compose": "*" - "@types/node": "*" - checksum: 57d809e42350c9ddefa2150306355e40757877468bb027e0bd99f5aeb43cfaf8ba8b14761ea65e419d6fb4c2403a1f3ed0762872a9cf040dbd14357caca56548 - languageName: node - linkType: hard - -"@types/koa__router@npm:12.0.3": - version: 12.0.3 - resolution: "@types/koa__router@npm:12.0.3" - dependencies: - "@types/koa": "*" - checksum: e9cdc53e01a6b2340583e94982cec2720c2d4c582240438eca57db7db4596f707578ac3e32cd32ace787331de304b6292cca8c98b0233c77f8749493c4991c96 - languageName: node - linkType: hard - "@types/lodash.clonedeep@npm:^4": version: 4.5.7 resolution: "@types/lodash.clonedeep@npm:4.5.7" @@ -25125,11 +24952,11 @@ __metadata: linkType: hard "@types/memcached@npm:^2.2.6": - version: 2.2.6 - resolution: "@types/memcached@npm:2.2.6" + version: 2.2.10 + resolution: "@types/memcached@npm:2.2.10" dependencies: "@types/node": "*" - checksum: 26426743c734b76bccf7ea60341e6f58c585f625e3687e749b0623224513ee39557df27715260d2bf2b45d75a940f77c227675d4ccf3720d88f5a949e5429220 + checksum: c95e2ed494d5df5e45bab024d24ff2ba45930eb9737cb86564a5ac2a0b3fb5dfdc23d8a65061da38ffe2aabe202a8d333764c0c3dc99d2bb205bff8ba620f2c2 languageName: node linkType: hard @@ -25982,7 +25809,7 @@ __metadata: languageName: node linkType: hard -"@types/tedious@npm:^4.0.10": +"@types/tedious@npm:^4.0.14": version: 4.0.14 resolution: "@types/tedious@npm:4.0.14" dependencies: @@ -27928,13 +27755,6 @@ __metadata: languageName: node linkType: hard -"ansi-color@npm:^0.2.1": - version: 0.2.1 - resolution: "ansi-color@npm:0.2.1" - checksum: f3b809a91db1b2ec869a3bf5c0af13a4a8fa971d69a3404852b09d27e7789e1ca885ecd61d7c36f446d9c9f04980393ee099f9d02223d588a0dae19be033c4f3 - languageName: node - linkType: hard - "ansi-colors@npm:3.2.3": version: 3.2.3 resolution: "ansi-colors@npm:3.2.3" @@ -31440,7 +31260,7 @@ __metadata: languageName: node linkType: hard -"bs-logger@npm:0.x": +"bs-logger@npm:^0.2.6": version: 0.2.6 resolution: "bs-logger@npm:0.2.6" dependencies: @@ -31564,18 +31384,6 @@ __metadata: languageName: node linkType: hard -"bufrw@npm:^1.3.0": - version: 1.3.0 - resolution: "bufrw@npm:1.3.0" - dependencies: - ansi-color: ^0.2.1 - error: ^7.0.0 - hexer: ^1.5.0 - xtend: ^4.0.0 - checksum: e0cdfae2d1f4c0a2ffdc4e352ce3dbd547c4683c76072d48b98322945c318cbb0b6c2ccb5719d7de14abbe2076d68796f7d905b9b2c859fa29259fe66894b6c6 - languageName: node - linkType: hard - "builtin-modules@npm:^3.1.0": version: 3.2.0 resolution: "builtin-modules@npm:3.2.0" @@ -36891,16 +36699,6 @@ __metadata: languageName: node linkType: hard -"error@npm:7.0.2": - version: 7.0.2 - resolution: "error@npm:7.0.2" - dependencies: - string-template: ~0.2.1 - xtend: ~4.0.0 - checksum: 407ff5faa73f5da3424a81d0160a1d3c6b5144e87cb1266334e7a4c2c7a69ae653e1b544032d7dbd8b210006858eea909ea0f46694b0484cd7555ba3086be0a8 - languageName: node - linkType: hard - "error@npm:^7.0.0": version: 7.2.1 resolution: "error@npm:7.2.1" @@ -41109,7 +40907,7 @@ fsevents@~2.1.1: rimraf: ^6.0.1 supertest: ^7.0.0 tailwindcss: 3.4.3 - ts-jest: ^29.2.3 + ts-jest: ^29.2.5 ts-morph: ^24.0.0 tslib: ^2.6.2 typescript: ^5.5.3 @@ -42115,23 +41913,17 @@ fsevents@~2.1.1: "@nx/react": 19.5.3 "@nx/storybook": 19.5.3 "@nx/workspace": 19.5.3 - "@opentelemetry/api": ^1.8.0 - "@opentelemetry/auto-instrumentations-node": ^0.44.0 - "@opentelemetry/context-zone": ^1.23.0 - "@opentelemetry/context-zone-peer-dep": ^1.23.0 - "@opentelemetry/exporter-jaeger": ^1.23.0 - "@opentelemetry/exporter-trace-otlp-http": ^0.50.0 - "@opentelemetry/instrumentation-document-load": ^0.37.0 - "@opentelemetry/instrumentation-fetch": ^0.50.0 - "@opentelemetry/instrumentation-user-interaction": ^0.37.0 - "@opentelemetry/instrumentation-xml-http-request": ^0.50.0 - "@opentelemetry/sdk-trace-base": ^1.23.0 - "@opentelemetry/sdk-trace-node": ^1.23.0 - "@opentelemetry/sdk-trace-web": ^1.23.0 + "@opentelemetry/api": ^1.9.0 + "@opentelemetry/auto-instrumentations-node": ^0.49.0 + "@opentelemetry/sdk-trace-base": ^1.26.0 + "@opentelemetry/sdk-trace-node": ^1.26.0 + "@opentelemetry/sdk-trace-web": ^1.26.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@paypal/react-paypal-js": ^8.7.0 "@radix-ui/react-form": ^0.1.0 "@radix-ui/react-tooltip": ^1.1.2 "@sentry/browser": ^8.35.0 + "@sentry/nestjs": ^8.35.0 "@sentry/nextjs": ^8.35.0 "@sentry/node": ^8.35.0 "@sentry/opentelemetry": ^8.35.0 @@ -44427,20 +44219,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"hexer@npm:^1.5.0": - version: 1.5.0 - resolution: "hexer@npm:1.5.0" - dependencies: - ansi-color: ^0.2.1 - minimist: ^1.1.0 - process: ^0.10.0 - xtend: ^4.0.0 - bin: - hexer: ./cli.js - checksum: 2e7a919da953ae7bc8ee3d88b01615fd640a71f65cfaa8e7f0775f44ebbd06fe9d3b901a582c155a518537282dd231f5ca2f8523a5e7b84defc0b07f16854c22 - languageName: node - linkType: hard - "hexoid@npm:^1.0.0": version: 1.0.0 resolution: "hexoid@npm:1.0.0" @@ -45497,18 +45275,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"import-in-the-middle@npm:1.7.1": - version: 1.7.1 - resolution: "import-in-the-middle@npm:1.7.1" - dependencies: - acorn: ^8.8.2 - acorn-import-assertions: ^1.9.0 - cjs-module-lexer: ^1.2.2 - module-details-from-path: ^1.0.3 - checksum: 37cc8c75fb7eac60611bafafea7fc60f794d0931fdabcec516c8a26effe69e914b1f7e8116e98549c6fdd1fe88dcaebfdebf35d7f52c761b48b312e40f3bf323 - languageName: node - linkType: hard - "import-in-the-middle@npm:^1.11.2": version: 1.11.2 resolution: "import-in-the-middle@npm:1.11.2" @@ -47390,19 +47156,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"jaeger-client@npm:^3.15.0": - version: 3.19.0 - resolution: "jaeger-client@npm:3.19.0" - dependencies: - node-int64: ^0.4.0 - opentracing: ^0.14.4 - thriftrw: ^3.5.0 - uuid: ^8.3.2 - xorshift: ^1.1.1 - checksum: fcdc0523b70299c0db1c07e6c209fa170cef75aa3ad00e6241c906423ba07dc6112a60e1b96de59c2a8eb9d335bf0a0c2a23cc13595ef24aededca2fdff65837 - languageName: node - linkType: hard - "jake@npm:^10.6.1": version: 10.6.1 resolution: "jake@npm:10.6.1" @@ -51320,7 +51073,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"lodash.memoize@npm:4.x, lodash.memoize@npm:^4.1.2": +"lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" checksum: 9ff3942feeccffa4f1fafa88d32f0d24fdc62fd15ded5a74a5f950ff5f0c6f61916157246744c620173dddf38d37095a92327d5fd3861e2063e736a5c207d089 @@ -51540,13 +51293,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"long@npm:^2.4.0": - version: 2.4.0 - resolution: "long@npm:2.4.0" - checksum: e24fd5e14be90ba6ec3faa43b3b0f1c4ac88bfdc52471d90f63e173572f6db27c45873e847f8af58283ca3140eb42d7ba11708102f3cc0956793b03305c737e0 - languageName: node - linkType: hard - "long@npm:^4.0.0": version: 4.0.0 resolution: "long@npm:4.0.0" @@ -51844,7 +51590,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"make-error@npm:1.x, make-error@npm:^1.1.1": +"make-error@npm:^1.1.1, make-error@npm:^1.3.6": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 @@ -55851,13 +55597,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"opentracing@npm:^0.14.4": - version: 0.14.7 - resolution: "opentracing@npm:0.14.7" - checksum: 5f7e44439062d056a2a72ac89eff463c9cf5659a2aea230ff7f5a226c5e960c195ce04ec2e2cc590140bbb9c5d2be11a5a50a23484cbe2d0e132af4309d4c904 - languageName: node - linkType: hard - "opn@npm:5.4.0": version: 5.4.0 resolution: "opn@npm:5.4.0" @@ -59460,13 +59199,6 @@ fsevents@~2.1.1: languageName: node linkType: hard -"process@npm:^0.10.0": - version: 0.10.1 - resolution: "process@npm:0.10.1" - checksum: bdaaa28a8edf96d5daa0f5c1faf4adfedce512ebca829a82e846d991492780c34eb934decf4fa5b311c698881d07a8d4592b4d7ea53ec03d51580a2f364d3e30 - languageName: node - linkType: hard - "process@npm:^0.11.10, process@npm:~0.11.0": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -59613,7 +59345,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.3, protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": +"protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": version: 7.2.6 resolution: "protobufjs@npm:7.2.6" dependencies: @@ -59653,6 +59385,26 @@ fsevents@~2.1.1: languageName: node linkType: hard +"protobufjs@npm:^7.3.0": + version: 7.4.0 + resolution: "protobufjs@npm:7.4.0" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/node": ">=13.7.0" + long: ^5.0.0 + checksum: ba0e6b60541bbf818bb148e90f5eb68bd99004e29a6034ad9895a381cbd352be8dce5376e47ae21b2e05559f2505b4a5f4a3c8fa62402822c6ab4dcdfb89ffb3 + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.5": version: 2.0.6 resolution: "proxy-addr@npm:2.0.6" @@ -66857,19 +66609,6 @@ resolve@1.1.7: languageName: node linkType: hard -"thriftrw@npm:^3.5.0": - version: 3.12.0 - resolution: "thriftrw@npm:3.12.0" - dependencies: - bufrw: ^1.3.0 - error: 7.0.2 - long: ^2.4.0 - bin: - thrift2json: ./thrift2json.js - checksum: fc03374131a713f5c5eca712a4092abf48282d985d23542f3ef7c92d222af52716e7037c446d5734708cc514d8fad42674f82dd6a9c59b8807beba0a1169b539 - languageName: node - linkType: hard - "throat@npm:^6.0.1": version: 6.0.1 resolution: "throat@npm:6.0.1" @@ -67451,19 +67190,19 @@ resolve@1.1.7: languageName: node linkType: hard -"ts-jest@npm:^29.2.3": - version: 29.2.3 - resolution: "ts-jest@npm:29.2.3" +"ts-jest@npm:^29.2.3, ts-jest@npm:^29.2.5": + version: 29.2.5 + resolution: "ts-jest@npm:29.2.5" dependencies: - bs-logger: 0.x + bs-logger: ^0.2.6 ejs: ^3.1.10 - fast-json-stable-stringify: 2.x + fast-json-stable-stringify: ^2.1.0 jest-util: ^29.0.0 json5: ^2.2.3 - lodash.memoize: 4.x - make-error: 1.x - semver: ^7.5.3 - yargs-parser: ^21.0.1 + lodash.memoize: ^4.1.2 + make-error: ^1.3.6 + semver: ^7.6.3 + yargs-parser: ^21.1.1 peerDependencies: "@babel/core": ">=7.0.0-beta.0 <8" "@jest/transform": ^29.0.0 @@ -67484,7 +67223,7 @@ resolve@1.1.7: optional: true bin: ts-jest: cli.js - checksum: b405fe2f5f9b8aee8ec83520e91ce61a43b3025069dc59809cfbee7255ac1710d694a6bdfecc5bba5f365b9605d0520d62855d0c1bbf0665096e12a71ed3a642 + checksum: d60d1e1d80936f6002b1bb27f7e062408bc733141b9d666565503f023c340a3196d506c836a4316c5793af81a5f910ab49bb9c13f66e2dc66de4e0f03851dbca languageName: node linkType: hard @@ -71125,13 +70864,6 @@ resolve@1.1.7: languageName: node linkType: hard -"xorshift@npm:^1.1.1": - version: 1.2.0 - resolution: "xorshift@npm:1.2.0" - checksum: bb5575707d20a806e71fa3e80bc3dc083a4bcf3c82965bd27b797a355cf87583273fe7676cfc9c7ffaa4c17d5a171903462045e2154e51fe1f07b90a4dc6b9d2 - languageName: node - linkType: hard - "xregexp@npm:2.0.0": version: 2.0.0 resolution: "xregexp@npm:2.0.0" @@ -71254,7 +70986,7 @@ resolve@1.1.7: languageName: node linkType: hard -"yargs-parser@npm:21.1.1, yargs-parser@npm:>=21.1.1, yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:21.1.1, yargs-parser@npm:>=21.1.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c @@ -71537,15 +71269,6 @@ resolve@1.1.7: languageName: node linkType: hard -"zone.js@npm:^0.11.0 || ^0.13.0 || ^0.14.0": - version: 0.14.4 - resolution: "zone.js@npm:0.14.4" - dependencies: - tslib: ^2.3.0 - checksum: 6168114b44f3f984c9aa72b1d0a05706b7b3010a38bcb49ef89f338c217279db77be26d1fd79d3eeb59b63b223472b104851be81898094f5eefc550bd69ae68f - languageName: node - linkType: hard - "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4"