From d8e797b958b9ae46276ec5bfb89691066fc1b26d Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 6 May 2022 14:45:56 -0400 Subject: [PATCH 1/9] feat: set recursion detection header --- .../http/middleware/RecursionDetection.kt | 43 ++++++ .../http/middleware/RecursionDetectionTest.kt | 131 ++++++++++++++++++ .../core/AwsHttpBindingProtocolGenerator.kt | 2 + .../RecursionDetectionMiddleware.kt | 42 ++++++ 4 files changed, 218 insertions(+) create mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt create mode 100644 aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt create mode 100644 codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt new file mode 100644 index 00000000000..5e208ca11b5 --- /dev/null +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws.sdk.kotlin.runtime.http.middleware + +import aws.sdk.kotlin.runtime.InternalSdkApi +import aws.smithy.kotlin.runtime.http.operation.ModifyRequestMiddleware +import aws.smithy.kotlin.runtime.http.operation.SdkHttpRequest +import aws.smithy.kotlin.runtime.util.EnvironmentProvider +import aws.smithy.kotlin.runtime.util.text.urlEncodeComponent + +internal const val ENV_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" +internal const val ENV_TRACE_ID = "_X_AMZ_TRACE_ID" +internal const val HEADER_TRACE_ID = "X-Amzn-Trace-Id" + +/** + * HTTP middleware to add the recursion detection header where required. + */ +@InternalSdkApi +public class RecursionDetection( + private val env: EnvironmentProvider +) : ModifyRequestMiddleware { + override suspend fun modifyRequest(req: SdkHttpRequest): SdkHttpRequest { + if (req.subject.headers.contains(HEADER_TRACE_ID)) return req + + val traceId = env.getenv(ENV_TRACE_ID) + if (env.getenv(ENV_FUNCTION_NAME) == null || traceId == null) return req + + req.subject.headers[HEADER_TRACE_ID] = traceId.urlEncodeComponent(where = Char::isISOControl) + return req + } +} + +/** + * Identifies characters that require %-encoding for the purposes of this specific header. + * + * The existing `Char::isISOControl` doesn't apply here because that matches against characters in + * `[0x00, 0x1f] U [0x7f, 0x9f]`. The SEP for recursion detection dictates we should encode across + * `[0x00, 0x1f] U [0x7f, 0xff]`. + */ +private fun Char.isISOControl() = code in 0x00..0x1f || code in 0x7f..0xff diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt new file mode 100644 index 00000000000..1ac9f4e976f --- /dev/null +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws.sdk.kotlin.runtime.http.middleware + +import aws.sdk.kotlin.runtime.testing.TestPlatformProvider +import aws.smithy.kotlin.runtime.client.ExecutionContext +import aws.smithy.kotlin.runtime.http.Headers +import aws.smithy.kotlin.runtime.http.HttpBody +import aws.smithy.kotlin.runtime.http.HttpStatusCode +import aws.smithy.kotlin.runtime.http.engine.HttpClientEngineBase +import aws.smithy.kotlin.runtime.http.operation.* +import aws.smithy.kotlin.runtime.http.request.HttpRequest +import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder +import aws.smithy.kotlin.runtime.http.response.HttpCall +import aws.smithy.kotlin.runtime.http.response.HttpResponse +import aws.smithy.kotlin.runtime.http.sdkHttpClient +import aws.smithy.kotlin.runtime.time.Instant +import aws.smithy.kotlin.runtime.util.get +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +@OptIn(ExperimentalCoroutinesApi::class) +class RecursionDetectionTest { + private class TraceHeaderSerializer( + private val traceHeader: String + ) : HttpSerialize { + override suspend fun serialize(context: ExecutionContext, input: Unit): HttpRequestBuilder { + val builder = HttpRequestBuilder() + builder.headers[HEADER_TRACE_ID] = traceHeader + return builder + } + } + + private val mockEngine = object : HttpClientEngineBase("test") { + override suspend fun roundTrip(request: HttpRequest): HttpCall { + val resp = HttpResponse(HttpStatusCode.fromValue(200), Headers.Empty, HttpBody.Empty) + val now = Instant.now() + return HttpCall(request, resp, now, now) + } + } + + private val client = sdkHttpClient(mockEngine) + + private suspend fun test( + env: Map, + existingTraceHeader: String?, + expectedTraceHeader: String? + ) { + val op = SdkHttpOperation.build { + serializer = if (existingTraceHeader != null) TraceHeaderSerializer(existingTraceHeader) else UnitSerializer + deserializer = IdentityDeserializer + context { + service = "Test Service" + operationName = "testOperation" + } + } + + val provider = TestPlatformProvider(env) + op.install(RecursionDetection(provider)) + op.roundTrip(client, Unit) + + val request = op.context[HttpOperationContext.HttpCallList].last().request + if (expectedTraceHeader != null) { + assertEquals(expectedTraceHeader, request.headers[HEADER_TRACE_ID]) + } else { + assertFalse(request.headers.contains(HEADER_TRACE_ID)) + } + } + + @Test + fun `it noops if env unset`() = runTest { + test( + emptyMap(), + null, + null + ) + } + + @Test + fun `it sets header when both envs are present`() = runTest { + test( + mapOf( + ENV_FUNCTION_NAME to "some-function", + ENV_TRACE_ID to "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + ), + null, + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + ) + } + + @Test + fun `it noops if trace env set but no lambda env`() = runTest { + test( + mapOf( + ENV_TRACE_ID to "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + ), + null, + null + ) + } + + @Test + fun `it respects existing trace header`() = runTest { + test( + mapOf( + ENV_FUNCTION_NAME to "some-function", + ENV_TRACE_ID to "EnvValue" + ), + "OriginalValue", + "OriginalValue" + ) + } + + @Test + fun `it url encodes new trace header`() = runTest { + test( + mapOf( + ENV_FUNCTION_NAME to "some-function", + ENV_TRACE_ID to "first\nsecond" + ), + null, + "first%0Asecond" + ) + } +} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/core/AwsHttpBindingProtocolGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/core/AwsHttpBindingProtocolGenerator.kt index 53e4be8b4c1..dc76c16da42 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/core/AwsHttpBindingProtocolGenerator.kt +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/core/AwsHttpBindingProtocolGenerator.kt @@ -8,6 +8,7 @@ import aws.sdk.kotlin.codegen.AwsKotlinDependency import aws.sdk.kotlin.codegen.AwsRuntimeTypes import aws.sdk.kotlin.codegen.protocols.eventstream.EventStreamParserGenerator import aws.sdk.kotlin.codegen.protocols.eventstream.EventStreamSerializerGenerator +import aws.sdk.kotlin.codegen.protocols.middleware.RecursionDetectionMiddleware import aws.sdk.kotlin.codegen.protocols.middleware.ResolveAwsEndpointMiddleware import aws.sdk.kotlin.codegen.protocols.middleware.UserAgentMiddleware import aws.sdk.kotlin.codegen.protocols.protocoltest.AwsHttpProtocolUnitTestErrorGenerator @@ -48,6 +49,7 @@ abstract class AwsHttpBindingProtocolGenerator : HttpBindingProtocolGenerator() }.toMutableList() middleware.add(UserAgentMiddleware()) + middleware.add(RecursionDetectionMiddleware()) return middleware } diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt new file mode 100644 index 00000000000..8975e031b39 --- /dev/null +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package aws.sdk.kotlin.codegen.protocols.middleware + +import aws.sdk.kotlin.codegen.AwsKotlinDependency +import software.amazon.smithy.kotlin.codegen.core.KotlinDependency +import software.amazon.smithy.kotlin.codegen.core.KotlinWriter +import software.amazon.smithy.kotlin.codegen.model.buildSymbol +import software.amazon.smithy.kotlin.codegen.model.namespace +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware +import software.amazon.smithy.model.shapes.OperationShape + +/** + * HTTP middleware to add the recursion detection header where required. + */ +class RecursionDetectionMiddleware : ProtocolMiddleware { + override val name: String = "RecursionDetection" + override val order: Byte = 30 + + private val middlewareSymbol = buildSymbol { + name = "RecursionDetection" + namespace(AwsKotlinDependency.AWS_HTTP, subpackage = "middleware") + } + + private val platformSymbol = buildSymbol { + name = "Platform" + namespace(KotlinDependency.UTILS) + } + + override fun renderProperties(writer: KotlinWriter) { + writer.addImport(middlewareSymbol) + writer.addImport(platformSymbol) + } + + override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { + writer.write("op.install(#T(Platform::getenv))", middlewareSymbol) + } +} From 247364ad9a5c3487afe5b64ee452648937b3fcd4 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 9 May 2022 11:45:44 -0400 Subject: [PATCH 2/9] fix traceid env --- .../sdk/kotlin/runtime/http/middleware/RecursionDetection.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt index 5e208ca11b5..b0314cc288b 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -12,7 +12,7 @@ import aws.smithy.kotlin.runtime.util.EnvironmentProvider import aws.smithy.kotlin.runtime.util.text.urlEncodeComponent internal const val ENV_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" -internal const val ENV_TRACE_ID = "_X_AMZ_TRACE_ID" +internal const val ENV_TRACE_ID = "_X_AMZN_TRACE_ID" internal const val HEADER_TRACE_ID = "X-Amzn-Trace-Id" /** From c639bd3456ca980332cfd492f6dbecadcc49c32a Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 9 May 2022 14:27:31 -0400 Subject: [PATCH 3/9] move percentEncode into middleware, since it's SEP-specific --- .../http/middleware/RecursionDetection.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt index b0314cc288b..1eb1747b0d5 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -9,7 +9,7 @@ import aws.sdk.kotlin.runtime.InternalSdkApi import aws.smithy.kotlin.runtime.http.operation.ModifyRequestMiddleware import aws.smithy.kotlin.runtime.http.operation.SdkHttpRequest import aws.smithy.kotlin.runtime.util.EnvironmentProvider -import aws.smithy.kotlin.runtime.util.text.urlEncodeComponent +import aws.smithy.kotlin.runtime.util.text.percentEncode internal const val ENV_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" internal const val ENV_TRACE_ID = "_X_AMZN_TRACE_ID" @@ -28,16 +28,27 @@ public class RecursionDetection( val traceId = env.getenv(ENV_TRACE_ID) if (env.getenv(ENV_FUNCTION_NAME) == null || traceId == null) return req - req.subject.headers[HEADER_TRACE_ID] = traceId.urlEncodeComponent(where = Char::isISOControl) + req.subject.headers[HEADER_TRACE_ID] = traceId.percentEncode() return req } } /** - * Identifies characters that require %-encoding for the purposes of this specific header. + * Percent-encode ISO control characters for the purposes of this specific header. * - * The existing `Char::isISOControl` doesn't apply here because that matches against characters in + * The existing `Char::isISOControl` check cannot be used here, because that matches against characters in * `[0x00, 0x1f] U [0x7f, 0x9f]`. The SEP for recursion detection dictates we should encode across * `[0x00, 0x1f] U [0x7f, 0xff]`. */ -private fun Char.isISOControl() = code in 0x00..0x1f || code in 0x7f..0xff +private fun String.percentEncode(): String { + val sb = StringBuilder(this.length) + val data = this.encodeToByteArray() + for (cbyte in data) { + val chr = cbyte.toInt().toChar() + sb.append( + if (chr.code in 0x00..0x1f || chr.code in 0x7f..0xff) + cbyte.percentEncode() else chr + ) + } + return sb.toString() +} From cc56086a17a8d9187aba5b6235443db247efd2d5 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 9 May 2022 16:16:54 -0400 Subject: [PATCH 4/9] cleanup codegen piece --- .../runtime/http/middleware/RecursionDetection.kt | 3 ++- .../middleware/RecursionDetectionMiddleware.kt | 12 +----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt index 1eb1747b0d5..ed20d990919 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -9,6 +9,7 @@ import aws.sdk.kotlin.runtime.InternalSdkApi import aws.smithy.kotlin.runtime.http.operation.ModifyRequestMiddleware import aws.smithy.kotlin.runtime.http.operation.SdkHttpRequest import aws.smithy.kotlin.runtime.util.EnvironmentProvider +import aws.smithy.kotlin.runtime.util.Platform import aws.smithy.kotlin.runtime.util.text.percentEncode internal const val ENV_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" @@ -20,7 +21,7 @@ internal const val HEADER_TRACE_ID = "X-Amzn-Trace-Id" */ @InternalSdkApi public class RecursionDetection( - private val env: EnvironmentProvider + private val env: EnvironmentProvider = Platform ) : ModifyRequestMiddleware { override suspend fun modifyRequest(req: SdkHttpRequest): SdkHttpRequest { if (req.subject.headers.contains(HEADER_TRACE_ID)) return req diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt index 8975e031b39..b5d1f276412 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt @@ -26,17 +26,7 @@ class RecursionDetectionMiddleware : ProtocolMiddleware { namespace(AwsKotlinDependency.AWS_HTTP, subpackage = "middleware") } - private val platformSymbol = buildSymbol { - name = "Platform" - namespace(KotlinDependency.UTILS) - } - - override fun renderProperties(writer: KotlinWriter) { - writer.addImport(middlewareSymbol) - writer.addImport(platformSymbol) - } - override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { - writer.write("op.install(#T(Platform::getenv))", middlewareSymbol) + writer.write("op.install(#T())", middlewareSymbol) } } From 03fde7a34b117894e23e6c2453924dcf3fdec485 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 9 May 2022 16:17:25 -0400 Subject: [PATCH 5/9] remove unused import --- .../codegen/protocols/middleware/RecursionDetectionMiddleware.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt index b5d1f276412..da203ff67ee 100644 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt +++ b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/protocols/middleware/RecursionDetectionMiddleware.kt @@ -6,7 +6,6 @@ package aws.sdk.kotlin.codegen.protocols.middleware import aws.sdk.kotlin.codegen.AwsKotlinDependency -import software.amazon.smithy.kotlin.codegen.core.KotlinDependency import software.amazon.smithy.kotlin.codegen.core.KotlinWriter import software.amazon.smithy.kotlin.codegen.model.buildSymbol import software.amazon.smithy.kotlin.codegen.model.namespace From 647aadde6e536778c1108ffe2d531a403d0de582 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 9 May 2022 16:37:50 -0400 Subject: [PATCH 6/9] consume new percentEncodeTo --- .../runtime/http/middleware/RecursionDetection.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt index ed20d990919..5f0eda67ec5 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -10,7 +10,7 @@ import aws.smithy.kotlin.runtime.http.operation.ModifyRequestMiddleware import aws.smithy.kotlin.runtime.http.operation.SdkHttpRequest import aws.smithy.kotlin.runtime.util.EnvironmentProvider import aws.smithy.kotlin.runtime.util.Platform -import aws.smithy.kotlin.runtime.util.text.percentEncode +import aws.smithy.kotlin.runtime.util.text.percentEncodeTo internal const val ENV_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" internal const val ENV_TRACE_ID = "_X_AMZN_TRACE_ID" @@ -46,10 +46,11 @@ private fun String.percentEncode(): String { val data = this.encodeToByteArray() for (cbyte in data) { val chr = cbyte.toInt().toChar() - sb.append( - if (chr.code in 0x00..0x1f || chr.code in 0x7f..0xff) - cbyte.percentEncode() else chr - ) + if (chr.code in 0x00..0x1f || chr.code in 0x7f..0xff) { + cbyte.percentEncodeTo(sb) + } else { + sb.append(chr) + } } return sb.toString() } From df73fa181472eb434368a9d2b47ce7d537ef0d10 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Tue, 10 May 2022 11:43:14 -0400 Subject: [PATCH 7/9] changelog --- .changes/2d52d36d-564b-4e31-a7fa-14d8839d4a96.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/2d52d36d-564b-4e31-a7fa-14d8839d4a96.json diff --git a/.changes/2d52d36d-564b-4e31-a7fa-14d8839d4a96.json b/.changes/2d52d36d-564b-4e31-a7fa-14d8839d4a96.json new file mode 100644 index 00000000000..cec59aa1f0a --- /dev/null +++ b/.changes/2d52d36d-564b-4e31-a7fa-14d8839d4a96.json @@ -0,0 +1,5 @@ +{ + "id": "2d52d36d-564b-4e31-a7fa-14d8839d4a96", + "type": "feature", + "description": "Implement recursion detection middleware." +} \ No newline at end of file From bafb05cb78f594d780cf1146d21790c2fbe48c38 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Tue, 10 May 2022 12:55:45 -0400 Subject: [PATCH 8/9] update %encode rules per published SEP --- .../runtime/http/middleware/RecursionDetection.kt | 6 +++--- .../http/middleware/RecursionDetectionTest.kt | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt index 5f0eda67ec5..f05b15f3feb 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/middleware/RecursionDetection.kt @@ -38,15 +38,15 @@ public class RecursionDetection( * Percent-encode ISO control characters for the purposes of this specific header. * * The existing `Char::isISOControl` check cannot be used here, because that matches against characters in - * `[0x00, 0x1f] U [0x7f, 0x9f]`. The SEP for recursion detection dictates we should encode across - * `[0x00, 0x1f] U [0x7f, 0xff]`. + * `[0x00, 0x1f] U [0x7f, 0x9f]`. The SEP for recursion detection dictates we should only encode across + * `[0x00, 0x1f]`. */ private fun String.percentEncode(): String { val sb = StringBuilder(this.length) val data = this.encodeToByteArray() for (cbyte in data) { val chr = cbyte.toInt().toChar() - if (chr.code in 0x00..0x1f || chr.code in 0x7f..0xff) { + if (chr.code in 0x00..0x1f) { cbyte.percentEncodeTo(sb) } else { sb.append(chr) diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt index 1ac9f4e976f..1bf75bb8028 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt @@ -128,4 +128,16 @@ class RecursionDetectionTest { "first%0Asecond" ) } + + @Test + fun `ignores other chars that are usually %-encoded`() = runTest { + test( + mapOf( + ENV_FUNCTION_NAME to "some-function", + ENV_TRACE_ID to "test123-=;:+&[]{}\"'" + ), + null, + "test123-=;:+&[]{}\"'" + ) + } } From 822c1c3af9b9b663c7f80bef17c8ba59aee9d027 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Tue, 10 May 2022 14:24:01 -0400 Subject: [PATCH 9/9] remove dangerous chars --- .../kotlin/runtime/http/middleware/RecursionDetectionTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt index 1bf75bb8028..f5a497ad5a3 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/middleware/RecursionDetectionTest.kt @@ -130,7 +130,7 @@ class RecursionDetectionTest { } @Test - fun `ignores other chars that are usually %-encoded`() = runTest { + fun `ignores other chars that are usually percent encoded`() = runTest { test( mapOf( ENV_FUNCTION_NAME to "some-function",