Skip to content

Commit 1d875e4

Browse files
authored
Check for sensitive trait in Shape members (#181)
1 parent 5fb9184 commit 1d875e4

File tree

9 files changed

+221
-22
lines changed

9 files changed

+221
-22
lines changed

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

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void writeMembers(TypeScriptWriter writer, Shape shape) {
7373
void writeFilterSensitiveLog(TypeScriptWriter writer, String objectParam) {
7474
writer.write("...$L,", objectParam);
7575
for (MemberShape member : members) {
76-
if (isMemberOverwriteRequired(member)) {
76+
if (isMemberOverwriteRequired(member, new HashSet<String>())) {
7777
Shape memberTarget = model.expectShape(member.getTarget());
7878
String memberName = getSanitizedMemberName(member);
7979
String memberParam = String.format("%s.%s", objectParam, memberName);
@@ -139,7 +139,7 @@ private void writeCollectionFilterSensitiveLog(
139139
MemberShape mapMember = ((MapShape) collectionMemberTarget).getValue();
140140
writeMapFilterSensitiveLog(writer, mapMember, itemParam);
141141
} else {
142-
// This path should not reach because of recursive isIterationRequired.
142+
// This path should not reach because of recursive isMemberOverwriteRequired.
143143
throw new CodegenException(String.format(
144144
"CollectionFilterSensitiveLog attempted for %s while it was not required",
145145
collectionMemberTarget.getType()
@@ -180,7 +180,7 @@ private void writeMapFilterSensitiveLog(TypeScriptWriter writer, MemberShape map
180180
MemberShape nestedMapMember = ((MapShape) mapMemberTarget).getValue();
181181
writeMapFilterSensitiveLog(writer, nestedMapMember, valueParam);
182182
} else {
183-
// This path should not reach because of recursive isIterationRequired.
183+
// This path should not reach because of recursive isMemberOverwriteRequired.
184184
throw new CodegenException(String.format(
185185
"MapFilterSensitiveLog attempted for %s while it was not required",
186186
mapMemberTarget.getType()
@@ -196,37 +196,41 @@ private void writeMapFilterSensitiveLog(TypeScriptWriter writer, MemberShape map
196196
}
197197

198198
/**
199-
* Identifies if iteration is required on member.
199+
* Identifies if member needs to be overwritten in filterSensitiveLog.
200200
*
201-
* @param member a {@link MemberShape} to check for iteration required.
202-
* @return Returns true if the iteration is required on member.
201+
* @param member a {@link MemberShape} to check if overwrite is required.
202+
* @param parents a set of membernames which are parents of existing member to avoid unending recursion.
203+
* @return Returns true if the overwrite is required on member.
203204
*/
204-
private boolean isIterationRequired(MemberShape member) {
205+
private boolean isMemberOverwriteRequired(MemberShape member, Set<String> parents) {
206+
if (member.getMemberTrait(model, SensitiveTrait.class).isPresent()) {
207+
return true;
208+
}
209+
205210
Shape memberTarget = model.expectShape(member.getTarget());
211+
parents.add(symbolProvider.toMemberName(member));
206212
if (memberTarget instanceof StructureShape) {
207-
return true;
213+
Collection<MemberShape> structureMemberList = ((StructureShape) memberTarget).getAllMembers().values();
214+
for (MemberShape structureMember: structureMemberList) {
215+
if (!parents.contains(symbolProvider.toMemberName(structureMember))
216+
&& isMemberOverwriteRequired(structureMember, parents)) {
217+
return true;
218+
}
219+
}
208220
} else if (memberTarget instanceof CollectionShape) {
209221
MemberShape collectionMember = ((CollectionShape) memberTarget).getMember();
210-
return isIterationRequired(collectionMember);
222+
if (!parents.contains(symbolProvider.toMemberName(collectionMember))) {
223+
return isMemberOverwriteRequired(collectionMember, parents);
224+
}
211225
} else if (memberTarget instanceof MapShape) {
212226
MemberShape mapMember = ((MapShape) memberTarget).getValue();
213-
return isIterationRequired(mapMember);
227+
if (!parents.contains(symbolProvider.toMemberName(mapMember))) {
228+
return isMemberOverwriteRequired(mapMember, parents);
229+
}
214230
}
215231
return false;
216232
}
217233

218-
/**
219-
* Identifies if member needs to be overwritten in filterSensitiveLog.
220-
*
221-
* @param member a {@link MemberShape} to check if overwrite is required.
222-
* @return Returns true if the overwrite is required on member.
223-
*/
224-
private boolean isMemberOverwriteRequired(MemberShape member) {
225-
return (
226-
member.getMemberTrait(model, SensitiveTrait.class).isPresent() || isIterationRequired(member)
227-
);
228-
}
229-
230234
/**
231235
* Returns the member name to be used in generation.
232236
*

smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ public void filtersSensitiveSimpleShape() {
5454
+ " })\n");
5555
}
5656

57+
@Test
58+
public void skipsFilterForInsensitiveSimpleShape() {
59+
testStructureCodegen("test-insensitive-simple-shape.smithy",
60+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
61+
+ " ...obj,\n"
62+
+ " })\n");
63+
}
64+
5765
@Test
5866
public void callsFilterForStructureWithSensitiveData() {
5967
testStructureCodegen("test-structure-with-sensitive-data.smithy",
@@ -87,6 +95,14 @@ public void filtersSensitiveStructure() {
8795
+ " })\n");
8896
}
8997

98+
@Test
99+
public void skipsFilterForInsensitiveStructure() {
100+
testStructureCodegen("test-insensitive-structure.smithy",
101+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
102+
+ " ...obj,\n"
103+
+ " })\n");
104+
}
105+
90106
@Test
91107
public void filtersSensitiveMemberPointingToStructure() {
92108
testStructureCodegen("test-sensitive-member-pointing-to-structure.smithy",
@@ -123,6 +139,17 @@ public void callsFilterInListWithSensitiveData() {
123139
+ " })\n");
124140
}
125141

142+
@Test
143+
public void callsFilterForListWithSensitiveMember() {
144+
testStructureCodegen("test-list-with-sensitive-member.smithy",
145+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
146+
+ " ...obj,\n"
147+
+ " ...(obj.foo && { foo:\n"
148+
+ " SENSITIVE_STRING\n"
149+
+ " }),\n"
150+
+ " })\n");
151+
}
152+
126153
@Test
127154
public void filtersSensitiveList() {
128155
testStructureCodegen("test-sensitive-list.smithy",
@@ -134,6 +161,14 @@ public void filtersSensitiveList() {
134161
+ " })\n");
135162
}
136163

164+
@Test
165+
public void skipsFilterForInsensitiveList() {
166+
testStructureCodegen("test-insensitive-list.smithy",
167+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
168+
+ " ...obj,\n"
169+
+ " })\n");
170+
}
171+
137172
@Test
138173
public void filtersSensitiveMemberPointingToList() {
139174
testStructureCodegen("test-sensitive-member-pointing-to-list.smithy",
@@ -172,6 +207,17 @@ public void callsFilterInMapWithSensitiveData() {
172207
+ " })\n");
173208
}
174209

210+
@Test
211+
public void callsFilterForMapWithSensitiveMember() {
212+
testStructureCodegen("test-map-with-sensitive-member.smithy",
213+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
214+
+ " ...obj,\n"
215+
+ " ...(obj.foo && { foo:\n"
216+
+ " SENSITIVE_STRING\n"
217+
+ " }),\n"
218+
+ " })\n");
219+
}
220+
175221
@Test
176222
public void filtersSensitiveMap() {
177223
testStructureCodegen("test-sensitive-map.smithy",
@@ -194,6 +240,22 @@ public void filtersSensitiveMemberPointingToMap() {
194240
+ " })\n");
195241
}
196242

243+
@Test
244+
public void skipsFilterForInsensitiveMap() {
245+
testStructureCodegen("test-insensitive-map.smithy",
246+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
247+
+ " ...obj,\n"
248+
+ " })\n");
249+
}
250+
251+
@Test
252+
public void skipsFilterOnEncounteringRecursiveShapes() {
253+
testStructureCodegen("test-recursive-shapes.smithy",
254+
" export const filterSensitiveLog = (obj: GetFooInput): any => ({\n"
255+
+ " ...obj,\n"
256+
+ " })\n");
257+
}
258+
197259
private String testStructureCodegen(String file, String expectedType) {
198260
Model model = Model.assembler()
199261
.addImport(getClass().getResource(file))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: NamesList
13+
}
14+
15+
list NamesList {
16+
member: String
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: NamesMap
13+
}
14+
15+
map NamesMap {
16+
key: String,
17+
value: String
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
firstname: String,
13+
lastname: String
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: User
13+
}
14+
15+
structure User {
16+
firstname: String,
17+
lastname: String
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: PhoneNumbersList
13+
}
14+
15+
list PhoneNumbersList {
16+
@sensitive
17+
member: String
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: PhoneNumbersMap
13+
}
14+
15+
map PhoneNumbersMap {
16+
key: String,
17+
18+
@sensitive
19+
value: String
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace smithy.example
2+
3+
@protocols([{name: "aws.rest-json-1.1"}])
4+
service Example {
5+
version: "1.0.0",
6+
operations: [GetFoo]
7+
}
8+
9+
operation GetFoo(GetFooInput)
10+
11+
structure GetFooInput {
12+
foo: User
13+
}
14+
15+
structure User {
16+
recursiveUser: User,
17+
recursiveList: UsersList,
18+
recursiveMap: UsersMap
19+
}
20+
21+
list UsersList {
22+
member: User
23+
}
24+
25+
map UsersMap {
26+
key: String,
27+
value: User
28+
}

0 commit comments

Comments
 (0)