Skip to content

Commit 4b001eb

Browse files
authored
Add filterSensitiveLog for Union types (#190)
* Add writeFilterSensitiveLog to UnionGenerator with check for sensitive member * Return $undefined if no value matched in filterSensitiveLog * Remove fromEntries as TSConfig doesn't have it on type ObjectConstructor Can be fixed by adding es2017 in lib https://stackoverflow.com/questions/45422573 But waiting for root TSConfig to be pushed * Non-sensitive SimpleShape in Union.filterSensitiveLog * Add check for obj.$unknown defined in Union.filterSensitiveLog * Call writeStructureFilterSensitiveLog from Union * Union.filterSensitiveLog for complex members * Add check for UnionShape in filterSensitiveLog * Update documentation for UnionGenerator * Add newlines within Union components * Fix checkstyle errors * Test callsFilterForUnionWithSensitiveData * Test callsFilterInUnionWithSensitiveData * Test filtersSensitiveUnion * Test callsFilterForUnionWithoutSensitiveData * Test callsFilterInUnionWithoutSensitiveData * rename test-insensitive-structure * Test filtersSensitiveMemberPointingToUnion * Test callsFilterForListWithUnionWithSensitiveData * Test callsFilterInListWithUnionWithSensitiveData * Test callsFilterForMapWithUnionWithSensitiveData * Test callsFilterInMapWithUnionWithSensitiveData * Fix ordering of objects in callsFilterInUnionWithoutSensitiveData * Remove unused memberTarget instanceof UnionShape * Disable filterSensitiveLog for event stream * Rename STREAMING_TRAIT to STREAMING_CONTENT * Log unknown union member as 'UNKNOWN' * Use isStructureShape() and isUnionShape() instead of instanceof * Added newline at the end of .smithy test files * Add tests for complex shapes inside Union * Create new function writeMemberFilterSensitiveLog * Call writeMemberFilterSensitiveLog from Union * Call writeMemberFilterSensitiveLog from Collection/Member * Throw CodegenException in the else block of writeMemberFilterSensitiveLog
1 parent b6ff818 commit 4b001eb

16 files changed

+514
-96
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java

Lines changed: 60 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
import software.amazon.smithy.model.shapes.MapShape;
2626
import software.amazon.smithy.model.shapes.MemberShape;
2727
import software.amazon.smithy.model.shapes.Shape;
28+
import software.amazon.smithy.model.shapes.SimpleShape;
2829
import software.amazon.smithy.model.shapes.StructureShape;
2930
import software.amazon.smithy.model.traits.IdempotencyTokenTrait;
3031
import software.amazon.smithy.model.traits.SensitiveTrait;
32+
import software.amazon.smithy.model.traits.StreamingTrait;
3133

3234
/**
3335
* Generates objects, interfaces, enums, etc.
@@ -74,27 +76,37 @@ void writeFilterSensitiveLog(TypeScriptWriter writer, String objectParam) {
7476
writer.write("...$L,", objectParam);
7577
for (MemberShape member : members) {
7678
if (isMemberOverwriteRequired(member, new HashSet<String>())) {
77-
Shape memberTarget = model.expectShape(member.getTarget());
7879
String memberName = getSanitizedMemberName(member);
79-
String memberParam = String.format("%s.%s", objectParam, memberName);
8080
writer.openBlock("...($1L.$2L && { $2L: ", "}),", objectParam, memberName, () -> {
81-
if (member.getMemberTrait(model, SensitiveTrait.class).isPresent()) {
82-
// member is Sensitive, hide the value.
83-
writer.write("SENSITIVE_STRING");
84-
} else if (memberTarget instanceof StructureShape) {
85-
writeStructureFilterSensitiveLog(writer, memberTarget, memberParam);
86-
} else if (memberTarget instanceof CollectionShape) {
87-
MemberShape collectionMember = ((CollectionShape) memberTarget).getMember();
88-
writeCollectionFilterSensitiveLog(writer, collectionMember, memberParam);
89-
} else if (memberTarget instanceof MapShape) {
90-
MemberShape mapMember = ((MapShape) memberTarget).getValue();
91-
writeMapFilterSensitiveLog(writer, mapMember, memberParam);
92-
}
81+
String memberParam = String.format("%s.%s", objectParam, memberName);
82+
writeMemberFilterSensitiveLog(writer, member, memberParam);
9383
});
9484
}
9585
}
9686
}
9787

88+
void writeMemberFilterSensitiveLog(TypeScriptWriter writer, MemberShape member, String memberParam) {
89+
Shape memberTarget = model.expectShape(member.getTarget());
90+
if (member.getMemberTrait(model, SensitiveTrait.class).isPresent()) {
91+
// member is Sensitive, hide the value.
92+
writer.write("SENSITIVE_STRING");
93+
} else if (memberTarget instanceof SimpleShape) {
94+
writer.write(memberParam);
95+
} else if (memberTarget.isStructureShape() || memberTarget.isUnionShape()) {
96+
writeStructureFilterSensitiveLog(writer, memberTarget, memberParam);
97+
} else if (memberTarget instanceof CollectionShape) {
98+
MemberShape collectionMember = ((CollectionShape) memberTarget).getMember();
99+
writeCollectionFilterSensitiveLog(writer, collectionMember, memberParam);
100+
} else if (memberTarget instanceof MapShape) {
101+
MemberShape mapMember = ((MapShape) memberTarget).getValue();
102+
writeMapFilterSensitiveLog(writer, mapMember, memberParam);
103+
} else {
104+
throw new CodegenException(String.format(
105+
"MemberFilterSensitiveLog attempted for %s", memberTarget.getType()
106+
));
107+
}
108+
}
109+
98110
/**
99111
* Recursively writes filterSensitiveLog for StructureShape.
100112
*/
@@ -106,10 +118,13 @@ private void writeStructureFilterSensitiveLog(
106118
if (structureTarget.hasTrait(SensitiveTrait.class)) {
107119
// member is Sensitive, hide the value.
108120
writer.write("SENSITIVE_STRING");
109-
return;
121+
} else if (structureTarget.hasTrait(StreamingTrait.class) && structureTarget.isUnionShape()) {
122+
// disable logging for StreamingTrait
123+
writer.write("'STREAMING_CONTENT'");
124+
} else {
125+
// Call filterSensitiveLog on Structure.
126+
writer.write("$T.filterSensitiveLog($L)", symbolProvider.toSymbol(structureTarget), structureParam);
110127
}
111-
// Call filterSensitiveLog on Structure.
112-
writer.write("$T.filterSensitiveLog($L)", symbolProvider.toSymbol(structureTarget), structureParam);
113128
}
114129

115130
/**
@@ -123,32 +138,15 @@ private void writeCollectionFilterSensitiveLog(
123138
if (collectionMember.getMemberTrait(model, SensitiveTrait.class).isPresent()) {
124139
// member is Sensitive, hide the value.
125140
writer.write("SENSITIVE_STRING");
126-
return;
141+
} else if (model.expectShape(collectionMember.getTarget()) instanceof SimpleShape) {
142+
writer.write(collectionParam);
143+
} else {
144+
writer.openBlock("$L.map(", ")", collectionParam, () -> {
145+
String itemParam = "item";
146+
writer.write("$L => ", itemParam);
147+
writeMemberFilterSensitiveLog(writer, collectionMember, itemParam);
148+
});
127149
}
128-
129-
writer.openBlock("$L.map(", ")", collectionParam, () -> {
130-
String itemParam = "item";
131-
Shape collectionMemberTarget = model.expectShape(collectionMember.getTarget());
132-
writer.write("$L => ", itemParam);
133-
if (collectionMemberTarget instanceof StructureShape) {
134-
writeStructureFilterSensitiveLog(writer, collectionMemberTarget, itemParam);
135-
} else if (collectionMemberTarget instanceof CollectionShape) {
136-
MemberShape nestedCollectionMember = ((CollectionShape) collectionMemberTarget).getMember();
137-
writeCollectionFilterSensitiveLog(writer, nestedCollectionMember, itemParam);
138-
} else if (collectionMemberTarget instanceof MapShape) {
139-
MemberShape mapMember = ((MapShape) collectionMemberTarget).getValue();
140-
writeMapFilterSensitiveLog(writer, mapMember, itemParam);
141-
} else {
142-
// This path should not reach because of recursive isMemberOverwriteRequired.
143-
throw new CodegenException(String.format(
144-
"CollectionFilterSensitiveLog attempted for %s while it was not required",
145-
collectionMemberTarget.getType()
146-
));
147-
// For quick-fix in case of high severity issue:
148-
// comment out the exception above and uncomment the line below.
149-
// writer.write("$1L => $1L", itemParam);
150-
}
151-
});
152150
}
153151

154152
/**
@@ -158,41 +156,23 @@ private void writeMapFilterSensitiveLog(TypeScriptWriter writer, MemberShape map
158156
if (mapMember.getMemberTrait(model, SensitiveTrait.class).isPresent()) {
159157
// member is Sensitive, hide the value.
160158
writer.write("SENSITIVE_STRING");
161-
return;
159+
} else if (model.expectShape(mapMember.getTarget()) instanceof SimpleShape) {
160+
writer.write(mapParam);
161+
} else {
162+
String accParam = "acc"; // accumulator for the reducer
163+
String keyParam = "key"; // key of the Object.entries() key-value pair
164+
String valueParam = "value"; // value of the Object.entries() key-value pair
165+
166+
// Reducer is common to all shapes.
167+
writer.openBlock("Object.entries($L).reduce(($L: any, [$L, $L]: [string, $T]) => ({", "}), {})",
168+
mapParam, accParam, keyParam, valueParam, symbolProvider.toSymbol(mapMember), () -> {
169+
writer.write("...$L,", accParam);
170+
writer.openBlock("[$L]: ", ",", keyParam, () -> {
171+
writeMemberFilterSensitiveLog(writer, mapMember, valueParam);
172+
});
173+
}
174+
);
162175
}
163-
164-
String accParam = "acc"; // accumulator for the reducer
165-
String keyParam = "key"; // key of the Object.entries() key-value pair
166-
String valueParam = "value"; // value of the Object.entries() key-value pair
167-
168-
// Reducer is common to all shapes.
169-
writer.openBlock("Object.entries($L).reduce(($L: any, [$L, $L]: [string, $T]) => ({", "}), {})",
170-
mapParam, accParam, keyParam, valueParam, symbolProvider.toSymbol(mapMember), () -> {
171-
writer.write("...$L,", accParam);
172-
Shape mapMemberTarget = model.expectShape(mapMember.getTarget());
173-
writer.openBlock("[$L]: ", ",", keyParam, () -> {
174-
if (mapMemberTarget instanceof StructureShape) {
175-
writeStructureFilterSensitiveLog(writer, mapMemberTarget, valueParam);
176-
} else if (mapMemberTarget instanceof CollectionShape) {
177-
MemberShape collectionMember = ((CollectionShape) mapMemberTarget).getMember();
178-
writeCollectionFilterSensitiveLog(writer, collectionMember, valueParam);
179-
} else if (mapMemberTarget instanceof MapShape) {
180-
MemberShape nestedMapMember = ((MapShape) mapMemberTarget).getValue();
181-
writeMapFilterSensitiveLog(writer, nestedMapMember, valueParam);
182-
} else {
183-
// This path should not reach because of recursive isMemberOverwriteRequired.
184-
throw new CodegenException(String.format(
185-
"MapFilterSensitiveLog attempted for %s while it was not required",
186-
mapMemberTarget.getType()
187-
));
188-
// For quick-fix in case of high severity issue:
189-
// comment out the exception above and uncomment the line below.
190-
// writer.write("$L", valueParam);
191-
}
192-
193-
});
194-
}
195-
);
196176
}
197177

198178
/**
@@ -209,7 +189,10 @@ private boolean isMemberOverwriteRequired(MemberShape member, Set<String> parent
209189

210190
Shape memberTarget = model.expectShape(member.getTarget());
211191
parents.add(symbolProvider.toMemberName(member));
212-
if (memberTarget instanceof StructureShape) {
192+
if (memberTarget.isUnionShape()) {
193+
// always call filterSensitiveLog for UnionShape
194+
return true;
195+
} else if (memberTarget.isStructureShape()) {
213196
Collection<MemberShape> structureMemberList = ((StructureShape) memberTarget).getAllMembers().values();
214197
for (MemberShape structureMember: structureMemberList) {
215198
if (!parents.contains(symbolProvider.toMemberName(structureMember))

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/UnionGenerator.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@
101101
* if (value.bear !== undefined) return visitor.bear(value.bear);
102102
* return visitor._(value.$unknown[0], value.$unknown[1]);
103103
* }
104+
* export const filterSensitiveLog = (obj: Attacker) => {
105+
* if (obj.lion !== undefined)
106+
* return { lion: Lion.filterSensitiveLog(obj.lion) };
107+
* if (obj.tiger !== undefined)
108+
* return { tiger: Tiger.filterSensitiveLog(obj.tiger) };
109+
* if (obj.bear !== undefined)
110+
* return { bear: Bear.filterSensitiveLog(obj.bear) };
111+
* if (obj.$unknown !== undefined)
112+
* return { [obj.$unknown[0]]: 'UNKNOWN' };
113+
* }
104114
* }
105115
* }</pre>
106116
*
@@ -148,13 +158,15 @@ public void run() {
148158
writeUnionMemberInterfaces();
149159
writeVisitorType();
150160
writeVisitorFunction();
161+
writeFilterSensitiveLog();
151162
});
152163
}
153164

154165
private void writeUnionMemberInterfaces() {
155166
writer.openBlock("interface $$Base {", "}", () -> {
156167
writer.write("__type?: $S;", shape.getId().getName());
157168
});
169+
writer.write("");
158170

159171
for (MemberShape member : shape.getAllMembers().values()) {
160172
String name = variantMap.get(member.getMemberName());
@@ -170,6 +182,7 @@ private void writeUnionMemberInterfaces() {
170182
}
171183
writer.write("$$unknown?: never;");
172184
});
185+
writer.write("");
173186
}
174187

175188
// Write out the unknown variant.
@@ -179,6 +192,7 @@ private void writeUnionMemberInterfaces() {
179192
}
180193
writer.write("$$unknown: [string, any];");
181194
});
195+
writer.write("");
182196
}
183197

184198
private void writeVisitorType() {
@@ -189,6 +203,7 @@ private void writeVisitorType() {
189203
}
190204
writer.write("_: (name: string, value: any) => T;");
191205
});
206+
writer.write("");
192207
}
193208

194209
private void writeVisitorFunction() {
@@ -203,5 +218,28 @@ private void writeVisitorFunction() {
203218
}
204219
writer.write("return visitor._(value.$$unknown[0], value.$$unknown[1]);");
205220
writer.dedent().write("}");
221+
writer.write("");
222+
}
223+
224+
private void writeFilterSensitiveLog() {
225+
String objectParam = "obj";
226+
writer.openBlock("export const filterSensitiveLog = ($L: $L): any => {", "}",
227+
objectParam, symbol.getName(),
228+
() -> {
229+
for (MemberShape member : shape.getAllMembers().values()) {
230+
String memberName = symbolProvider.toMemberName(member);
231+
StructuredMemberWriter structuredMemberWriter = new StructuredMemberWriter(
232+
model, symbolProvider, shape.getAllMembers().values());
233+
writer.openBlock("if (${1L}.${2L} !== undefined) return {${2L}: ", "};",
234+
objectParam, memberName, () -> {
235+
String memberParam = String.format("%s.%s", objectParam, memberName);
236+
structuredMemberWriter.writeMemberFilterSensitiveLog(writer, member, memberParam);
237+
}
238+
);
239+
}
240+
writer.write("if (${1L}.$$unknown !== undefined) return {[${1L}.$$unknown[0]]: 'UNKNOWN'};",
241+
objectParam);
242+
}
243+
);
206244
}
207245
}

0 commit comments

Comments
 (0)