Merge pull request #16884 from mozilla/FXA-9289

bug(graphql): Prevent reporting of known auth-server errors
This commit is contained in:
Dan Schomburg 2024-05-07 15:29:55 -07:00 коммит произвёл GitHub
Родитель 1569f134b5 74c32be9e3
Коммит 41ad4acd9a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 177 добавлений и 32 удалений

Просмотреть файл

@ -15,7 +15,11 @@ import {
import * as Sentry from '@sentry/node';
import { Transaction } from '@sentry/types';
import { isApolloError, processException } from '../reporting';
import {
isApolloError,
isAuthServerError,
processException,
} from '../reporting';
@Injectable()
export class SentryInterceptor implements NestInterceptor {
@ -43,6 +47,10 @@ export class SentryInterceptor implements NestInterceptor {
return;
}
}
// Skip known auth-server errors
if (isAuthServerError(exception)) return;
// Skip ApolloErrors
if (isApolloError(exception)) return;

Просмотреть файл

@ -3,7 +3,55 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import * as uuid from 'uuid';
import { FILTERED } from './pii/filter-actions';
import { filterObject } from './reporting';
import { filterObject, isAuthServerError } from './reporting';
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('filterObject', () => {
it('should be defined', () => {

Просмотреть файл

@ -56,6 +56,23 @@ export function isApolloError(err: Error): boolean {
return false;
}
/**
* 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 isOriginallyHttpError(
error: Error & { originalError?: { status: number } }
): boolean {

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import * as uuid from 'uuid';
import { filterObject } from './reporting';
import { filterObject, isAuthServerError } from './reporting';
const FILTERED = '[Filtered]';
@ -11,6 +11,54 @@ function getUid() {
return uuid.v4().replace(/-/g, '');
}
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('filterObject', () => {
it('should be defined', () => {
expect(filterObject).toBeDefined();
@ -19,25 +67,7 @@ describe('filterObject', () => {
// Test Sentry QueryParams filtering types
it('should filter array of key/value arrays', () => {
const input = {
extra: [
['foo', getUid()],
['baz', getUid()],
['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: getUid(),
baz: getUid(),
@ -45,6 +75,28 @@ describe('filterObject', () => {
},
};
const expected = {
type: undefined,
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: getUid(),
baz: getUid(),
bar: 'fred',
},
};
const expected = {
type: undefined,
extra: {
foo: FILTERED,
baz: FILTERED,
@ -57,18 +109,20 @@ describe('filterObject', () => {
it('should skip nested arrays that are not valid key/value arrays', () => {
const input = {
extra: [
['foo', getUid()],
['bar', 'fred'],
['fizz', 'buzz', 'parrot'],
],
type: undefined,
extra: {
foo: getUid(),
bar: 'fred',
fizz: ['buzz', 'parrot'],
},
};
const expected = {
extra: [
['foo', FILTERED],
['bar', 'fred'],
['fizz', 'buzz', 'parrot'],
],
type: undefined,
extra: {
foo: FILTERED,
bar: 'fred',
fizz: ['buzz', 'parrot'],
},
};
const output = filterObject(input);
expect(output).toEqual(expected);

Просмотреть файл

@ -49,6 +49,24 @@ export function isApolloError(err: Error): boolean {
return false;
}
/**
* 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' &&
error.extensions.errno >= 100
);
}
export function isOriginallyHttpError(
error: Error & { originalError?: { status: number } }
): boolean {