Skip to content

Commit c39792b

Browse files
authored
Fix maps/lists with sensitive members not redacted (#3765)
## Motivation and Context <!--- Why is this change required? What problem does it solve? --> <!--- If it fixes an open issue, please link to the issue here --> Lists and maps with sensitive members were not being properly redacted. Closes #3757 ## Description <!--- Describe your changes in detail --> Updated `Shape.shouldRedact` method to properly check for list and map shapes with sensitive members. Also updated the test definition so it would actually run since previously the test code was generated in a nested function inside a no-op function and never run. ## Testing <!--- Please describe in detail how you tested your changes --> <!--- Include details of your testing environment, and the tests you ran to --> <!--- see how your change affects other areas of the code, etc. --> Added test cases for lists with sensitive members, maps with sensitive keys, and maps with sensitive values. ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent c63e792 commit c39792b

File tree

6 files changed

+122
-31
lines changed

6 files changed

+122
-31
lines changed

CHANGELOG.next.toml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,40 @@
99
# message = "Fix typos in module documentation for generated crates"
1010
# references = ["smithy-rs#920"]
1111
# meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"}
12-
# author = "rcoh"
12+
# author = "rcoh"
13+
14+
[[smithy-rs]]
15+
message = "Support `stringArray` type in endpoints params"
16+
references = ["smithy-rs#3742"]
17+
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"}
18+
author = "landonxjames"
19+
20+
[[smithy-rs]]
21+
message = "Add support for `operationContextParams` Endpoints trait"
22+
references = ["smithy-rs#3755"]
23+
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"}
24+
author = "landonxjames"
25+
26+
[[aws-sdk-rust]]
27+
message = "`aws_smithy_runtime_api::client::orchestrator::HttpRequest` and `aws_smithy_runtime_api::client::orchestrator::HttpResponse` are now re-exported in AWS SDK clients so that using these types does not require directly depending on `aws-smithy-runtime-api`."
28+
references = ["smithy-rs#3591"]
29+
meta = { "breaking" = false, "tada" = false, "bug" = false }
30+
author = "ysaito1001"
31+
32+
[[smithy-rs]]
33+
message = "`aws_smithy_runtime_api::client::orchestrator::HttpRequest` and `aws_smithy_runtime_api::client::orchestrator::HttpResponse` are now re-exported in generated clients so that using these types does not require directly depending on `aws-smithy-runtime-api`."
34+
references = ["smithy-rs#3591"]
35+
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" }
36+
author = "ysaito1001"
37+
38+
[[aws-sdk-rust]]
39+
message = "Fix incorrect redaction of `@sensitive` types in maps and lists."
40+
references = ["smithy-rs#3765", "smithy-rs#3757"]
41+
meta = { "breaking" = false, "tada" = false, "bug" = true }
42+
author = "landonxjames"
43+
44+
[[smithy-rs]]
45+
message = "Fix incorrect redaction of `@sensitive` types in maps and lists."
46+
references = ["smithy-rs#3765", "smithy-rs#3757"]
47+
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }
48+
author = "landonxjames"

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.isOptional
4545
import software.amazon.smithy.rust.codegen.core.smithy.makeOptional
4646
import software.amazon.smithy.rust.codegen.core.smithy.rustType
4747
import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait
48+
import software.amazon.smithy.rust.codegen.core.util.REDACTION
4849
import software.amazon.smithy.rust.codegen.core.util.dq
4950
import software.amazon.smithy.rust.codegen.core.util.hasTrait
5051
import software.amazon.smithy.rust.codegen.core.util.letIf
5152
import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary
53+
import software.amazon.smithy.rust.codegen.core.util.shouldRedact
5254
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
5355

5456
// TODO(https://github.com/smithy-lang/smithy-rs/issues/1401) This builder generator is only used by the client.
@@ -353,7 +355,16 @@ class BuilderGenerator(
353355
rust("""let mut formatter = f.debug_struct(${builderName.dq()});""")
354356
members.forEach { member ->
355357
val memberName = symbolProvider.toMemberName(member)
356-
val fieldValue = member.redactIfNecessary(model, "self.$memberName")
358+
// If the struct is marked sensitive all fields get redacted, otherwise each field is determined on its own
359+
val fieldValue =
360+
if (shape.shouldRedact(model)) {
361+
REDACTION
362+
} else {
363+
member.redactIfNecessary(
364+
model,
365+
"self.$memberName",
366+
)
367+
}
357368

358369
rust(
359370
"formatter.field(${memberName.dq()}, &$fieldValue);",

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizat
3333
import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata
3434
import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom
3535
import software.amazon.smithy.rust.codegen.core.smithy.rustType
36+
import software.amazon.smithy.rust.codegen.core.util.REDACTION
3637
import software.amazon.smithy.rust.codegen.core.util.dq
3738
import software.amazon.smithy.rust.codegen.core.util.getTrait
3839
import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary
40+
import software.amazon.smithy.rust.codegen.core.util.shouldRedact
3941

4042
/** StructureGenerator customization sections */
4143
sealed class StructureSection(name: String) : Section(name) {
@@ -102,9 +104,19 @@ open class StructureGenerator(
102104
) {
103105
writer.rustBlock("fn fmt(&self, f: &mut #1T::Formatter<'_>) -> #1T::Result", RuntimeType.stdFmt) {
104106
rust("""let mut formatter = f.debug_struct(${name.dq()});""")
107+
105108
members.forEach { member ->
106109
val memberName = symbolProvider.toMemberName(member)
107-
val fieldValue = member.redactIfNecessary(model, "self.$memberName")
110+
// If the struct is marked sensitive all fields get redacted, otherwise each field is determined on its own
111+
val fieldValue =
112+
if (shape.shouldRedact(model)) {
113+
REDACTION
114+
} else {
115+
member.redactIfNecessary(
116+
model,
117+
"self.$memberName",
118+
)
119+
}
108120

109121
rust(
110122
"formatter.field(${memberName.dq()}, &$fieldValue);",

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import software.amazon.smithy.aws.traits.ServiceTrait
99
import software.amazon.smithy.codegen.core.CodegenException
1010
import software.amazon.smithy.model.Model
1111
import software.amazon.smithy.model.shapes.BooleanShape
12+
import software.amazon.smithy.model.shapes.ListShape
13+
import software.amazon.smithy.model.shapes.MapShape
1214
import software.amazon.smithy.model.shapes.MemberShape
1315
import software.amazon.smithy.model.shapes.NumberShape
1416
import software.amazon.smithy.model.shapes.OperationShape
@@ -84,13 +86,12 @@ fun ServiceShape.hasEventStreamOperations(model: Model): Boolean =
8486
}
8587

8688
fun Shape.shouldRedact(model: Model): Boolean =
87-
when (this) {
88-
is MemberShape ->
89-
model.expectShape(target).shouldRedact(model) ||
90-
model.expectShape(container)
91-
.shouldRedact(model)
92-
93-
else -> this.hasTrait<SensitiveTrait>()
89+
when {
90+
hasTrait<SensitiveTrait>() -> true
91+
this is MemberShape -> model.expectShape(target).shouldRedact(model)
92+
this is ListShape -> member.shouldRedact(model)
93+
this is MapShape -> key.shouldRedact(model) || value.shouldRedact(model)
94+
else -> false
9495
}
9596

9697
const val REDACTION = "\"*** Sensitive Data Redacted ***\""

codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ internal class BuilderGeneratorTest {
124124
.username("admin")
125125
.password("pswd")
126126
.secret_key("12345");
127-
assert_eq!(format!("{:?}", builder), "Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }");
127+
assert_eq!(format!("{:?}", builder),
128+
"Builder { username: Some(\"admin\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\", secret_value_map: \"*** Sensitive Data Redacted ***\", secret_key_map: \"*** Sensitive Data Redacted ***\", secret_list: \"*** Sensitive Data Redacted ***\" }");
128129
""",
129130
)
130131
}
@@ -278,18 +279,21 @@ internal class BuilderGeneratorTest {
278279
implBlock(provider.toSymbol(inner)) {
279280
BuilderGenerator.renderConvenienceMethod(this, provider, inner)
280281
}
281-
unitTest("test", additionalAttributes = listOf(Attribute.DenyDeprecated), block = {
282-
rust(
283-
// Notice that the builder is instantiated directly, and not through the Inner::builder() method.
284-
// This is because Inner is marked with `deprecated`, so any usage of `Inner` inside the test will
285-
// fail the compilation.
286-
//
287-
// This piece of code would fail though if the Builder inherits the attributes from Inner.
288-
"""
289-
let _ = test_inner::Builder::default();
290-
""",
291-
)
292-
})
282+
unitTest(
283+
"test", additionalAttributes = listOf(Attribute.DenyDeprecated),
284+
block = {
285+
rust(
286+
// Notice that the builder is instantiated directly, and not through the Inner::builder() method.
287+
// This is because Inner is marked with `deprecated`, so any usage of `Inner` inside the test will
288+
// fail the compilation.
289+
//
290+
// This piece of code would fail though if the Builder inherits the attributes from Inner.
291+
"""
292+
let _ = test_inner::Builder::default();
293+
""",
294+
)
295+
},
296+
)
293297
}
294298
project.withModule(provider.moduleForBuilder(inner)) {
295299
BuilderGenerator(model, provider, inner, emptyList()).render(this)

codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ class StructureGeneratorTest {
6262
structure Credentials {
6363
username: String,
6464
password: Password,
65-
66-
secretKey: SecretKey
65+
secretKey: SecretKey,
66+
secretValueMap: MapThatContainsSecretValues,
67+
secretKeyMap: MapThatContainsSecretKeys,
68+
secretList: ListThatContainsSecrets
6769
}
6870
6971
structure StructWithDoc {
@@ -79,6 +81,20 @@ class StructureGeneratorTest {
7981
public: String,
8082
private: SecretStructure,
8183
}
84+
85+
map MapThatContainsSecretKeys {
86+
key: SecretKey
87+
value: String
88+
}
89+
90+
map MapThatContainsSecretValues {
91+
key: String
92+
value: SecretKey
93+
}
94+
95+
list ListThatContainsSecrets {
96+
member: Password
97+
}
8298
""".asSmithyModel()
8399
val struct = model.lookup<StructureShape>("com.test#MyStruct")
84100
val structWithDoc = model.lookup<StructureShape>("com.test#StructWithDoc")
@@ -159,15 +175,27 @@ class StructureGeneratorTest {
159175
TestWorkspace.testProject().unitTest {
160176
structureGenerator(model, provider, this, credentials).render()
161177

162-
this.unitTest(
163-
"sensitive_fields_redacted",
178+
rust(
164179
"""
180+
use std::collections::HashMap;
181+
182+
let mut secret_map = HashMap::new();
183+
secret_map.insert("FirstSecret".to_string(), "don't leak me".to_string());
184+
secret_map.insert("SecondSecret".to_string(), "don't leak me".to_string());
185+
186+
let secret_list = vec!["don't leak me".to_string()];
187+
165188
let creds = Credentials {
166189
username: Some("not_redacted".to_owned()),
167190
password: Some("don't leak me".to_owned()),
168-
secret_key: Some("don't leak me".to_owned())
191+
secret_key: Some("don't leak me".to_owned()),
192+
secret_key_map: Some(secret_map.clone()),
193+
secret_value_map: Some(secret_map),
194+
secret_list: Some(secret_list),
169195
};
170-
assert_eq!(format!("{:?}", creds), "Credentials { username: Some(\"not_redacted\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\" }");
196+
197+
assert_eq!(format!("{:?}", creds),
198+
"Credentials { username: Some(\"not_redacted\"), password: \"*** Sensitive Data Redacted ***\", secret_key: \"*** Sensitive Data Redacted ***\", secret_value_map: \"*** Sensitive Data Redacted ***\", secret_key_map: \"*** Sensitive Data Redacted ***\", secret_list: \"*** Sensitive Data Redacted ***\" }");
171199
""",
172200
)
173201
}.compileAndTest()
@@ -179,8 +207,7 @@ class StructureGeneratorTest {
179207
TestWorkspace.testProject().unitTest {
180208
structureGenerator(model, provider, this, secretStructure).render()
181209

182-
this.unitTest(
183-
"sensitive_structure_redacted",
210+
rust(
184211
"""
185212
let secret_structure = SecretStructure {
186213
secret_field: Some("secret".to_owned()),

0 commit comments

Comments
 (0)