Skip to content

Commit

Permalink
Add more thorough obfuscation of test mocks to obfuscate cookies. Fix…
Browse files Browse the repository at this point in the history
… mock matching for classic authentication.
  • Loading branch information
phalestrivir committed Jul 25, 2024
1 parent 8afaffe commit 68a7f1e
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 68 deletions.
172 changes: 104 additions & 68 deletions src/utils/PollyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,89 +22,125 @@ export function defaultMatchRequestsBy(protocol: boolean = true) {
export function filterRecording(recording: {
request: {
headers: [{ name: string; value: string }];
postData: { text: any };
postData: { mimeType: string; text: any };
};
response: {
content: { mimeType: string; text: any };
cookies: {
httpOnly: boolean;
name: string;
path: string;
value: string;
}[];
headers: [{ name: string; value: string }];
};
response: { content: { mimeType: string; text: any } };
}) {
// request headers
if (recording.request?.headers) {
const headers: [{ name: string; value: string }] =
recording.request.headers;
headers.map((header) => {
if (header.name.toUpperCase() === 'AUTHORIZATION') {
if (isBase64Encoded(header.value)) {
header.value = encode('username:password');
} else {
header.value = header.value.replace(
/Bearer .+/,
'Bearer <bearer token>'
);
}
}
if (header.name.toUpperCase() === 'X-API-KEY') {
header.value = '<api key>';
}
if (header.name.toUpperCase() === 'X-API-SECRET') {
header.value = '<api secret>';
}
});
recording.request.headers = headers;
recording.request.headers.forEach(obfuscateHeader);
}

// request post body
if (recording.request?.postData?.text) {
let body = recording.request.postData.text;
body = body.replace(/assertion=.+?&/, 'assertion=<assertion jwt token>&');
recording.request.postData.text = body;
obfuscateData(recording.request.postData);
}

// response cookies
if (recording.response?.cookies) {
for (const cookie of recording.response.cookies) {
cookie.value = '<cookie>';
}
}

// response headers
if (recording.response?.headers) {
recording.response.headers.forEach(obfuscateHeader);
}

// response body
if (recording.response?.content?.text) {
let body = recording.response.content.text;
// JSON content
if (
recording.response.content.mimeType === 'application/json;charset=UTF-8'
) {
try {
const json = JSON.parse(body, (key, value) => {
if (key === 'access_token') return '<access token>';
if (key === 'id_token') return '<id token>';
if (key === 'accessKey') return '<access key>';
return value;
});
if (json.result) {
for (const obj of json.result) {
// check for scripts
if (obj.script) {
try {
let script = decode(obj.script);
script = script.replace(
/(var .*?(?:Sid|sid|Secret|secret|PhoneNumberFrom) = (?:"|'))(.*?)((?:"|'))/g,
'$1<secret>$3'
);
obj.script = encode(script);
} catch (error) {
//
}
}
obfuscateData(recording.response.content);
}
}

function obfuscateHeader(header: { name: string; value: string }): void {
if (header.name.toUpperCase() === 'AUTHORIZATION') {
if (isBase64Encoded(header.value)) {
header.value = encode('username:password');
} else {
header.value = header.value.replace(/Bearer .+/, 'Bearer <bearer token>');
}
}
if (header.name.toUpperCase() === 'X-API-KEY') {
header.value = '<api key>';
}
if (header.name.toUpperCase() === 'X-API-SECRET') {
header.value = '<api secret>';
}
if (header.name.toUpperCase() === 'COOKIE') {
header.value = header.value.replace(/=[^;]*/g, '=<cookie>');
}
if (header.name.toUpperCase() === 'SET-COOKIE') {
// The first attribute should always be the cookie in this header, so only replace that attribute
header.value = header.value.replace(/=[^;]*/, '=<cookie>');
}
}

function obfuscateData(data: { mimeType: string; text: any }): void {
// JSON content
if (data.mimeType.startsWith('application/json')) {
data.text = obfuscateJsonString(data.text);
// XML content
} else if (data.mimeType.startsWith('text/xml')) {
data.text = obfuscateXmlString(data.text);
// Form data content
} else if (data.mimeType.startsWith('application/x-www-form-urlencoded')) {
data.text = data.text.replace(
/assertion=.+?&/,
'assertion=<assertion jwt token>&'
);
}
}

function obfuscateJsonString(json: string): string {
try {
const jsonObj = JSON.parse(json, (key, value) => {
if (key === 'access_token') return '<access token>';
if (key === 'id_token') return '<id token>';
if (key === 'tokenId') return '<token id>';
if (key === 'accessKey') return '<access key>';
return value;
});
if (jsonObj.result) {
for (const obj of jsonObj.result) {
// check for scripts
if (obj.script) {
try {
let script = decode(obj.script);
script = script.replace(
/(var .*?(?:Sid|sid|Secret|secret|PhoneNumberFrom) = (?:"|'))(.*?)((?:"|'))/g,
'$1<secret>$3'
);
obj.script = encode(script);
} catch (error) {
// ignore
}
}
body = JSON.stringify(json);
} catch (error) {
// ignore
}
}
// Text and XML content
if (recording.response.content.mimeType === 'text/xml;charset=utf-8') {
try {
body = body.replace(
/<ds:X509Certificate>.+?<\/ds:X509Certificate>/gs,
`<ds:X509Certificate>${encode('<certificate>')}</ds:X509Certificate>`
);
} catch (error) {
// ignore
}
}
recording.response.content.text = body;
return JSON.stringify(jsonObj);
} catch (error) {
// ignore
}
}

function obfuscateXmlString(xml: string): string {
try {
return xml.replace(
/<ds:X509Certificate>.+?<\/ds:X509Certificate>/gs,
`<ds:X509Certificate>${encode('<certificate>')}</ds:X509Certificate>`
);
} catch (error) {
// ignore
}
}
7 changes: 7 additions & 0 deletions src/utils/SetupPollyForFrodoLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,13 @@ export function setupPollyForFrodoLib({
polly.server
.any('/am/json/*')
.recordingName(`${getFrodoCommand({ state })}/am`);
polly.server
.any(['/am/json/*/authenticate', '/am/json/*/sessions'])
.on('request', (req) => {
req.configure({
matchRequestsBy: authenticationMatchRequestsBy(),
});
});
polly.server
.any('/am/saml2/*')
.recordingName(`${getFrodoCommand({ state })}/saml2`);
Expand Down

0 comments on commit 68a7f1e

Please sign in to comment.