Skip to content

Commit

Permalink
Force redact all & Redact by Default
Browse files Browse the repository at this point in the history
  • Loading branch information
aklarfeld committed Apr 19, 2024
1 parent 2df66bf commit bb2e0ed
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 55 deletions.
18 changes: 17 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const defaultConfig = {
logResponseHeaders: true,
logResponseBody: true,
ignoredDomains: [],
forceRedactAll: false,
redactByDefault: false,
allowedDomains: [],
cacheTtl: 0,

// After the close command is sent, wait for this many milliseconds before
// exiting. This gives any hanging responses a chance to return.
Expand All @@ -38,6 +42,16 @@ const errors = {
'No Client Secret Provided, set SUPERGOOD_CLIENT_SECRET or pass it as an argument'
};

const SensitiveKeyActions = {
REDACT: 'REDACT',
ALLOW: 'ALLOW'
};

const EndpointActions = {
ALLOW: 'Allow',
IGNORE: 'Ignore'
}

const TestErrorPath = '/api/supergood-test-error';
const LocalClientId = 'local-client-id';
const LocalClientSecret = 'local-client-secret';
Expand All @@ -47,5 +61,7 @@ export {
errors,
TestErrorPath,
LocalClientId,
LocalClientSecret
LocalClientSecret,
SensitiveKeyActions,
EndpointActions
};
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ const Supergood = () => {

const responseArray = prepareData(
responseCacheValues as EventRequestType[],
supergoodConfig.remoteConfig,
supergoodConfig,
) as Array<EventRequestType>;

let data = [...responseArray];
Expand All @@ -310,7 +310,7 @@ const Supergood = () => {
if (force) {
const requestArray = prepareData(
requestCacheValues as EventRequestType[],
supergoodConfig.remoteConfig
supergoodConfig
) as Array<EventRequestType>;
data = [...requestArray, ...responseArray];
}
Expand Down
17 changes: 10 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ interface ConfigType {
allowedDomains: string[];
allowLocalUrls: boolean;
allowIpAddresses: boolean;
cacheTtl: number;
keysToHash: string[];
remoteConfigFetchEndpoint: string; // Defaults to {baseUrl}/config if not provided
eventSinkEndpoint: string; // Defaults to {baseUrl}/events if not provided
errorSinkEndpoint: string; // Defaults to {baseUrl}/errors if not provided
Expand All @@ -57,6 +55,8 @@ interface ConfigType {
logRequestBody: boolean;
logResponseHeaders: boolean;
logResponseBody: boolean;
forceRedactAll: boolean;
redactByDefault: boolean;
}

interface TelemetryType {
Expand All @@ -69,7 +69,7 @@ interface EndpointConfigType {
location: string;
regex: string;
ignored: boolean;
sensitiveKeys: Array<string>;
sensitiveKeys: Array<{ keyPath: string, action: string }>;
}

interface RemoteConfigType {
Expand All @@ -85,19 +85,20 @@ interface MetadataType {
serviceName?: string;
}

type TagType = Record<string, string | number | string[]>;

interface EventRequestType {
request: RequestType;
response: ResponseType;
tags?: Record<string, string | number | string[]>;
tags?: TagType;
metadata?: {
sensitiveKeys: Array<SensitiveKeyMetadata>;
tags?: Record<string, string | number | string[]>;
tags?: TagType;
};
}

type SupergoodContext = {
tags: Record<string, string | number | string[]>;
tags: TagType;
};

// interface EventResponseType {}
Expand Down Expand Up @@ -147,6 +148,7 @@ type RemoteConfigPayloadType = Array<{
sensitiveKeys: Array<
{
keyPath: string;
action: string;
}>;
}
}>;
Expand Down Expand Up @@ -174,5 +176,6 @@ export type {
RemoteConfigPayloadType,
MetadataType,
TelemetryType,
SupergoodContext
SupergoodContext,
TagType
};
151 changes: 135 additions & 16 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { RequestType, ResponseType } from './types';
import { RequestType, ResponseType, ConfigType } from './types';
import {
prepareData,
expandSensitiveKeySetForArrays,
redactValuesFromKeys
} from './utils';
import { defaultConfig, SensitiveKeyActions, EndpointActions } from './constants';
import { get as _get } from 'lodash';

it('generates multiple sensitive key paths for an array', () => {
Expand Down Expand Up @@ -34,13 +35,13 @@ it('generates multiple sensitive key paths for an array', () => {
]
}
};
const sensitiveKeys = ['blog.posts[].title'];
const sensitiveKeys = [{ keyPath: 'blog.posts[].title', action: SensitiveKeyActions.REDACT}];
expect(expandSensitiveKeySetForArrays(obj, sensitiveKeys)).toEqual([
'blog.posts[0].title',
'blog.posts[1].title',
'blog.posts[2].title',
'blog.posts[3].title'
]);
].map((key) => ({ keyPath: key, action: SensitiveKeyActions.REDACT })));
});

it('generates multiple sensitive key paths for an object with nested arrays', () => {
Expand Down Expand Up @@ -129,7 +130,7 @@ it('generates multiple sensitive key paths for an object with nested arrays', ()
]
}
};
const sensitiveKeys = ['blog.posts[].comments[].body'];
const sensitiveKeys = [{ keyPath: 'blog.posts[].comments[].body', action: SensitiveKeyActions.REDACT }];
expect(expandSensitiveKeySetForArrays(obj, sensitiveKeys)).toEqual([
'blog.posts[0].comments[0].body',
'blog.posts[0].comments[1].body',
Expand All @@ -141,7 +142,7 @@ it('generates multiple sensitive key paths for an object with nested arrays', ()
'blog.posts[2].comments[0].body',
'blog.posts[2].comments[1].body',
'blog.posts[3].comments[0].body'
]);
].map((key) => ({ keyPath: key, action: SensitiveKeyActions.REDACT })));
});

it('redacts values from keys with proper marshalling', () => {
Expand Down Expand Up @@ -189,12 +190,13 @@ it('redacts values from keys with proper marshalling', () => {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: ['requestBody.posts[].title']
sensitiveKeys: [{ keyPath: 'requestBody.posts[].title', action: SensitiveKeyActions.REDACT }]
}
}
};

const redactedObj = redactValuesFromKeys(obj, remoteConfig);
const config = { remoteConfig, ...defaultConfig } as ConfigType;
const redactedObj = redactValuesFromKeys(obj, config);
expect(_get(redactedObj, 'event.request.body.posts[0].title')).toBeNull();
expect(redactedObj.sensitiveKeyMetadata[0]).toEqual({
keyPath: 'requestBody.posts[0].title',
Expand Down Expand Up @@ -306,12 +308,12 @@ it('redacts values from keys of nested array', () => {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: ['requestBody.posts[].comments[].body']
sensitiveKeys: [{ keyPath: 'requestBody.posts[].comments[].body', action: SensitiveKeyActions.REDACT }]
}
}
};

const redactedObj = redactValuesFromKeys(obj, remoteConfig);
const config = { remoteConfig, ...defaultConfig } as ConfigType;
const redactedObj = redactValuesFromKeys(obj, config);
expect(
_get(redactedObj, 'event.request.body.posts[0].comments[0].body')
).toBeNull();
Expand Down Expand Up @@ -346,12 +348,12 @@ it('will not blow up or redact anything if the sensitive key is bad', () => {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: ['request_body.posts[].title[]']
sensitiveKeys: [{ keyPath: 'request_body.posts[].title[]', action: SensitiveKeyActions.REDACT }]
}
}
};

const redactedObj = redactValuesFromKeys(obj, remoteConfig);
const config = { remoteConfig, ...defaultConfig } as ConfigType;
const redactedObj = redactValuesFromKeys(obj, config);
expect(_get(redactedObj, 'event.request.body.name')).toBeTruthy();
expect(redactedObj.sensitiveKeyMetadata.length).toEqual(0);
});
Expand Down Expand Up @@ -395,13 +397,130 @@ it('will prepare the data appropriately for posting to the server', () => {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: ['responseBody.user.email', 'requestBody.blogType.name']
sensitiveKeys: [
{ keyPath: 'responseBody.user.email', action: SensitiveKeyActions.REDACT},
{ keyPath: 'requestBody.blogType.name', action: SensitiveKeyActions.REDACT}
]
}
}
};

const events = prepareData([obj], remoteConfig);
const config = { remoteConfig, ...defaultConfig } as ConfigType;
const events = prepareData([obj], config);
expect(_get(events[0], 'response.body.user.email')).toBeFalsy();
expect(_get(events[0], 'request.body.blogType.name')).toBeFalsy();
expect(events[0].metadata.sensitiveKeys.length).toEqual(2);
});

it('will force redact all keys if the config is set to do so', () => {
const MOCK_DATA_SERVER = 'http://localhost:3001';
const obj = {
request: {
id: '',
headers: {},
method: 'GET',
url: `${MOCK_DATA_SERVER}/posts`,
path: '/posts',
search: '',
requestedAt: new Date(),
body: {
blogType: {
name: 'My Blog'
}
}
},
response: {
headers: {},
status: 200,
statusText: 'OK',
respondedAt: new Date(),
body: {
name: 'My Blog',
user: {
name: 'John Doe',
email: '[email protected]'
},
comments: [{ id: 7, comment: 'good blog'}, { id: 8, comment: 'bad blog'}]
}
}
};
const remoteConfig = {
[new URL(MOCK_DATA_SERVER).hostname]: {
'/posts': {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: []
}
}
};
const config = { remoteConfig, ...defaultConfig, forceRedactAll: true } as ConfigType;
const events = prepareData([obj], config);
expect(_get(events[0], 'request.body.blogType.name')).toBeFalsy();
expect(_get(events[0], 'response.body.name')).toBeFalsy();
expect(_get(events[0], 'response.body.user.name')).toBeFalsy();
expect(_get(events[0], 'response.body.user.email')).toBeFalsy();
expect(_get(events[0], 'response.body.comments[0].id')).toBeFalsy();
expect(_get(events[0], 'response.body.comments[0].comment')).toBeFalsy();
expect(_get(events[0], 'response.body.comments[1].id')).toBeFalsy();
expect(_get(events[0], 'response.body.comments[1].comment')).toBeFalsy();
expect(events[0].metadata.sensitiveKeys.length).toEqual(8);
});

it('will redact by default if the config is set to do so', () => {
const MOCK_DATA_SERVER = 'http://localhost:3001';
const obj = {
request: {
id: '',
headers: {},
method: 'GET',
url: `${MOCK_DATA_SERVER}/posts`,
path: '/posts',
search: '',
requestedAt: new Date(),
body: {
blogType: {
name: 'My Blog'
}
}
},
response: {
headers: {},
status: 200,
statusText: 'OK',
respondedAt: new Date(),
body: {
name: 'My Blog',
user: {
name: 'John Doe',
email: '[email protected]'
},
comments: [{ id: 7, comment: 'good blog'}, { id: 8, comment: 'bad blog'}]
}
}
};
const remoteConfig = {
[new URL(MOCK_DATA_SERVER).hostname]: {
'/posts': {
location: 'path',
regex: '/posts',
ignored: false,
sensitiveKeys: [
{ keyPath: 'responseBody.user.email', action: SensitiveKeyActions.ALLOW },
{ keyPath: 'requestBody.blogType.name', action: SensitiveKeyActions.REDACT },
{ keyPath: 'responseBody.comments[].id', action: SensitiveKeyActions.ALLOW }
]
}
}
};
const config = { remoteConfig, ...defaultConfig, redactByDefault: true } as ConfigType;
const events = prepareData([obj], config);
expect(_get(events[0], 'request.body.blogType.name')).toBeFalsy();
expect(_get(events[0], 'response.body.name')).toBeFalsy();
expect(_get(events[0], 'response.body.user.name')).toBeFalsy();
expect(_get(events[0], 'response.body.user.email')).toBeTruthy();
expect(_get(events[0], 'response.body.comments[0].id')).toBeTruthy();
expect(_get(events[0], 'response.body.comments[0].comment')).toBeFalsy();
expect(_get(events[0], 'response.body.comments[1].id')).toBeTruthy();
expect(_get(events[0], 'response.body.comments[1].comment')).toBeFalsy();
expect(events[0].metadata.sensitiveKeys.length).toEqual(5);
});
Loading

0 comments on commit bb2e0ed

Please sign in to comment.