Skip to content

Commit 4a3d10c

Browse files
Fix redactor infinite loop on circular reference (#371)
1 parent b2d373f commit 4a3d10c

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed

packages/databricks-sdk-js/src/Redactor.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,25 @@ describe(__filename, () => {
3232
const str = "1234567890";
3333
assert.equal(onlyNBytes(str, n), "12345...(5 more bytes)");
3434
});
35+
36+
it("should handle circular objects", () => {
37+
const a: any = {};
38+
a["a"] = a;
39+
a["b"] = "b";
40+
41+
const actual = defaultRedactor.sanitize(a);
42+
const expected = {
43+
a: "circular ref",
44+
b: "b",
45+
};
46+
assert.deepEqual(actual, expected);
47+
});
48+
49+
it("should handle circular lists", () => {
50+
const a: any[] = [1, 2];
51+
a.push(a);
52+
const actual = defaultRedactor.sanitize(a);
53+
const expected = [1, 2, "circular ref"];
54+
assert.deepEqual(actual, expected);
55+
});
3556
});

packages/databricks-sdk-js/src/Redactor.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ function isPrimitveType(obj: any) {
1010

1111
export class Redactor {
1212
constructor(private fieldNames: string[] = []) {}
13-
1413
addFieldName(fieldName: string) {
1514
this.fieldNames.push(fieldName);
1615
}
1716

18-
sanitize(obj?: any, dropFields: string[] = []): any {
17+
sanitize(
18+
obj?: any,
19+
dropFields: string[] = [],
20+
seen: Set<any> = new Set()
21+
): any {
22+
if (seen.has(obj)) {
23+
return `circular ref`;
24+
}
25+
seen.add(obj);
1926
if (obj === undefined) {
2027
return undefined;
2128
}
@@ -24,24 +31,25 @@ export class Redactor {
2431
return obj;
2532
}
2633
if (Array.isArray(obj)) {
27-
return obj.map((e) => this.sanitize(e, dropFields));
34+
return obj.map((e) => this.sanitize(e, dropFields, seen));
2835
}
36+
2937
//make a copy of the object
30-
obj = Object.assign({}, obj);
31-
for (const key in obj) {
38+
const copyObj = Object.assign({}, obj);
39+
for (const key in copyObj) {
3240
if (dropFields.includes(key)) {
33-
delete obj[key];
41+
delete copyObj[key];
3442
} else if (
3543
isPrimitveType(obj[key]) &&
3644
this.fieldNames.includes(key)
3745
) {
38-
obj[key] = "***REDACTED***";
46+
copyObj[key] = "***REDACTED***";
3947
} else {
40-
obj[key] = this.sanitize(obj[key], dropFields);
48+
copyObj[key] = this.sanitize(obj[key], dropFields, seen);
4149
}
4250
}
4351

44-
return obj;
52+
return copyObj;
4553
}
4654
}
4755

0 commit comments

Comments
 (0)