Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(utils): make data redacters settings case insensitive #48

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,834 changes: 1,683 additions & 1,151 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"@types/jaeger-client": "^3.18.4",
"@types/jest": "^29.2.3",
"@types/node": "^18.11.9",
"@types/pino": "^7.0.5",
"axios": "^1.4.0",
"eslint": "^8.28.0",
"husky": "^8.0.2",
Expand Down
1 change: 1 addition & 0 deletions src/lib/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export const REQUEST_ID_HEADER = 'x-request-id';
export const REQUEST_ID_PARAM_NAME = 'requestId';
export const USER_ID_PARAM_NAME = 'userId';
export const TRACE_KEY = 'uber-trace-id';
export const REDACTED_STRING = '[REDACTED]';
6 changes: 5 additions & 1 deletion src/lib/utils/redact-sensitive-headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ export default function prepareSensitiveHeadersRedacter(
return inputHeaders;
}

const headersWithSensitiveUrlsLowered = headersWithSensitiveUrls.map((name) =>
name.toLowerCase(),
);

const redactSensitiveKeys = prepareSensitiveKeysRedacter(sensitiveHeaders);

const result = redactSensitiveKeys(inputHeaders) as IncomingHttpHeaders;

Object.keys(result).forEach((headerName) => {
if (headersWithSensitiveUrls.includes(headerName.toLowerCase())) {
if (headersWithSensitiveUrlsLowered.includes(headerName.toLowerCase())) {
result[headerName] = redactSensitiveQueryParams(result[headerName] as string);
}
});
Expand Down
6 changes: 4 additions & 2 deletions src/lib/utils/redact-sensitive-keys.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {Dict} from '../../types';
import {REDACTED_STRING} from '../consts';

export type SensitiveKeysRedacter = (inputObject: Dict) => Dict;

export function prepareSensitiveKeysRedacter(keysToRemove: string[] = []) {
const loweredKeysToRemove = keysToRemove.map((key) => key.toLowerCase());
const redactSensitiveKeys: SensitiveKeysRedacter = (inputObject: Dict) => {
return Object.keys(inputObject).reduce((result, key) => {
if (keysToRemove.includes(key.toLowerCase())) {
result[key] = '[REDACTED]';
if (loweredKeysToRemove.includes(key.toLowerCase())) {
result[key] = REDACTED_STRING;
} else {
result[key] = inputObject[key];
}
Expand Down
4 changes: 1 addition & 3 deletions src/tests/batch.test.ts → src/tests/utils/batch.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
'use strict';

import {DEFAULT_TICK_INTERVAL, prepareBatchedQueue} from '../lib/utils/batch';
import {DEFAULT_TICK_INTERVAL, prepareBatchedQueue} from '../../lib/utils/batch';

jest.useFakeTimers({legacyFakeTimers: true});

Expand Down
13 changes: 13 additions & 0 deletions src/tests/utils/is-true-env.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {isTrueEnvValue} from '../../lib/utils/is-true-env';

it('successfully checks value for truthfulness', () => {
expect(isTrueEnvValue('true')).toEqual(true);
expect(isTrueEnvValue('1')).toEqual(true);
});

it('successfully checks value for untruthfulness', () => {
expect(isTrueEnvValue('false')).toEqual(false);
expect(isTrueEnvValue('0')).toEqual(false);
expect(isTrueEnvValue('')).toEqual(false);
expect(isTrueEnvValue(undefined)).toEqual(false);
});
43 changes: 43 additions & 0 deletions src/tests/utils/redact-sensitive-headers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {REDACTED_STRING} from '../../lib/consts';
import prepareSensitiveHeadersRedacter from '../../lib/utils/redact-sensitive-headers';
import prepareSensitiveQueryParamsRedacter from '../../lib/utils/redact-sensitive-query-params';
import {NodeKit} from '../../nodekit';

it('correctly removes sensitive data from headers', () => {
const inputHeaders = {
Cookie: 'some-cookie-value',
SomeHeader: 'non-secret-header',
Referer: 'https://example.com/?someSecretParameter=secretValue',
};

const queryParamsRedacter = prepareSensitiveQueryParamsRedacter(['someSecretParameter']);
const headersRedacter = prepareSensitiveHeadersRedacter(
['cookie'],
['referer'],
queryParamsRedacter,
);

const redactedHeaders = headersRedacter(inputHeaders);

expect(redactedHeaders['Cookie']).toEqual(REDACTED_STRING);

const redactedRefererParams = new URL(redactedHeaders['Referer'] as string).searchParams;
expect(redactedRefererParams.get('someSecretParameter')).toEqual(REDACTED_STRING);
});

it('correctly removes sensitive data from headers using default config', () => {
const inputHeaders = {
Cookie: 'some-cookie-value',
SomeHeader: 'non-secret-header',
Referer: 'https://example.com/?token=secretValue',
};

const nk = new NodeKit({config: {appSensitiveQueryParams: ['token']}});

const redactedHeaders = nk.utils.redactSensitiveHeaders(inputHeaders);

expect(redactedHeaders['Cookie']).toEqual(REDACTED_STRING);

const redactedRefererParams = new URL(redactedHeaders['Referer'] as string).searchParams;
expect(redactedRefererParams.get('token')).toEqual(REDACTED_STRING);
});
97 changes: 97 additions & 0 deletions src/tests/utils/redact-sensitive-keys.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {REDACTED_STRING} from '../../lib/consts';
import {prepareSensitiveKeysRedacter} from '../../lib/utils/redact-sensitive-keys';
import {NodeKit} from '../../nodekit';
import {Dict} from '../../types';

function getTestData() {
return {
someString: 'lorem',
anotherString: 'ipsum',
someNumber: 100,
someObject: {
someValueInObject: 'hello',
},
verySensitiveValue: 42,
verySensitiveObject: {
someDataInSensitiveObject: 200,
},
someNonSensitiveObject: {
verySensitiveValue: 300,
},
VERYSENSITIVEVALUE: 42,
verysensitivevalue: 42,
};
}

function getTestConfiguration() {
return ['verysensitivevalue', 'verysensitiveobject'];
}

it('removes sensitive data from the input object', () => {
const redactSensitiveKeys = prepareSensitiveKeysRedacter(getTestConfiguration());
const redactedData = redactSensitiveKeys(getTestData());

expect(redactedData.verySensitiveValue).toEqual(REDACTED_STRING);
expect(redactedData.verySensitiveObject).toEqual(REDACTED_STRING);
});

it('removes sensitive keys regardless of their case', () => {
const redactSensitiveKeys = prepareSensitiveKeysRedacter(getTestConfiguration());
const redactedData = redactSensitiveKeys(getTestData());

expect(redactedData.VERYSENSITIVEVALUE).toEqual(REDACTED_STRING);
expect(redactedData.verysensitivevalue).toEqual(REDACTED_STRING);
});

it('removes sensitive keys regardless of case in configuration', () => {
const redactSensitiveKeys = prepareSensitiveKeysRedacter(
getTestConfiguration().map((s) => s.toUpperCase()),
);
const redactedData = redactSensitiveKeys(getTestData());

expect(redactedData.VERYSENSITIVEVALUE).toEqual(REDACTED_STRING);
expect(redactedData.verysensitivevalue).toEqual(REDACTED_STRING);
});

it('does not affect data inside objects', () => {
const redactSensitiveKeys = prepareSensitiveKeysRedacter(getTestConfiguration());
const redactedData = redactSensitiveKeys(getTestData());

expect((redactedData.someNonSensitiveObject as Dict).verySensitiveValue).toEqual(300);
});

it('contains default sensitive values', () => {
const nk = new NodeKit();

const inputData = {
nonSensitiveData: 42,
authorization: 'some-auth-token',
cookie: 'some-cookie',
};
const redactedData = nk.utils.redactSensitiveKeys(inputData);

expect(redactedData.nonSensitiveData).toEqual(42);
expect(redactedData.authorization).toEqual(REDACTED_STRING);
expect(redactedData.cookie).toEqual(REDACTED_STRING);
});

it('conbines default sensitive values with additional from configuration', () => {
const nk = new NodeKit({
config: {
appSensitiveKeys: ['appLevelSensitiveKey'],
},
});

const inputData = {
nonSensitiveData: 42,
authorization: 'some-auth-token',
cookie: 'some-cookie',
appLevelSensitiveKey: 'some-data',
};
const redactedData = nk.utils.redactSensitiveKeys(inputData);

expect(redactedData.nonSensitiveData).toEqual(42);
expect(redactedData.authorization).toEqual(REDACTED_STRING);
expect(redactedData.cookie).toEqual(REDACTED_STRING);
expect(redactedData.appLevelSensitiveKey).toEqual(REDACTED_STRING);
});
16 changes: 16 additions & 0 deletions src/tests/utils/redact-sensitive-query-params.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {REDACTED_STRING} from '../../lib/consts';
import prepareSensitiveQueryParamsRedacter from '../../lib/utils/redact-sensitive-query-params';

it('removes value of sensitive query parameters', () => {
const redactSensitiveQueryParams = prepareSensitiveQueryParamsRedacter(['someSensitiveKey']);
const inputUrl =
'https://example.com/some/path?foo=42&someSensitiveKey=sensitiveData&someOtherData=hello';

const redactedUrl = redactSensitiveQueryParams(inputUrl);
expect(redactedUrl.includes('sensitiveData')).toBe(false);

const redactedParams = new URL(redactedUrl).searchParams;
expect(redactedParams.get('foo')).toEqual('42');
expect(redactedParams.get('someOtherData')).toEqual('hello');
expect(redactedParams.get('someSensitiveKey')).toEqual(REDACTED_STRING);
});
Loading