From 9883173038cd0f9a6d5fc8584a19b259bb70f4e6 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 16 Sep 2022 15:11:52 +0000 Subject: [PATCH] Fix label sensitivity --- .../ServerHttpSensitivityGenerator.kt | 109 ++++++----- .../ServerHttpSensitivityGeneratorTest.kt | 49 ++--- .../aws-smithy-http-server/src/logging/mod.rs | 2 +- .../src/logging/sensitivity/request.rs | 26 +-- .../logging/sensitivity/uri/greedy_label.rs | 113 ----------- .../src/logging/sensitivity/uri/label.rs | 183 ++++++++++++++++-- .../src/logging/sensitivity/uri/mod.rs | 41 ++-- .../src/logging/service.rs | 2 +- 8 files changed, 272 insertions(+), 253 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/greedy_label.rs diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt index f2dfc16d7f..a6d0d8b3e2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt @@ -27,7 +27,6 @@ import software.amazon.smithy.rust.codegen.client.rustlang.Writable import software.amazon.smithy.rust.codegen.client.rustlang.asType import software.amazon.smithy.rust.codegen.client.rustlang.plus import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.rustlang.withBlock @@ -39,13 +38,6 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import java.util.* -internal fun findUriGreedyLabelPosition(uriPattern: UriPattern): Int? { - return uriPattern - .greedyLabel - .orElse(null) - ?.let { uriPattern.toString().indexOf("$it") } -} - /** Models the ways status codes can be bound and sensitive. */ class StatusCodeSensitivity(private val sensitive: Boolean, runtimeConfig: RuntimeConfig) { private val codegenScope = arrayOf( @@ -69,58 +61,69 @@ class StatusCodeSensitivity(private val sensitive: Boolean, runtimeConfig: Runti } } -/** Models the ways labels can be bound and sensitive. */ -sealed class LabelSensitivity(runtimeConfig: RuntimeConfig) { - private val codegenScope = arrayOf("SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(runtimeConfig).asType()) +data class GreedyLabel( + val index: Int, + val suffix: String, +) - class Normal(val indexes: List, runtimeConfig: RuntimeConfig) : LabelSensitivity(runtimeConfig) { - /** Returns the closure used during construction. */ - fun closure(): Writable = writable { - withBlock("{", "} as fn(_) -> _") { - rustBlock("|index: usize|") { - if (indexes.isNotEmpty()) { - withBlock("matches!(index,", ")") { - val matches = indexes.joinToString("|") { "$it" } - rust(matches) - } - } else { - rust("{_ = index; false}") - } - } - } +internal fun findGreedyLabel(uriPattern: UriPattern): GreedyLabel? = uriPattern + .segments + .asIterable() + .withIndex() + .find { (_, segment) -> + segment.isGreedyLabel + } + ?.let { (index, segment) -> + val remainingSegments = uriPattern.segments.asIterable().drop(index + 1) + val suffix = if (remainingSegments.isNotEmpty()) { + remainingSegments.joinToString(prefix = "/", separator = "/") + } else { + "" } + GreedyLabel(index, suffix) } - class Greedy(val suffixPosition: Int, runtimeConfig: RuntimeConfig) : LabelSensitivity(runtimeConfig) - fun hasRedactions(): Boolean = when (this) { - is Normal -> indexes.isNotEmpty() - is Greedy -> true +/** Models the ways labels can be bound and sensitive. */ +class LabelSensitivity(private val labelIndexes: List, private val greedyLabel: GreedyLabel?, runtimeConfig: RuntimeConfig) { + private val codegenScope = arrayOf("SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(runtimeConfig).asType()) + + /** Returns the closure used during construction. */ + fun closure(): Writable = writable { + rustTemplate( + """ + { + |index: usize| matches!(index, ${labelIndexes.joinToString("|")}) + } as fn(_) -> _ + """, + *codegenScope, + ) } + private fun hasRedactions(): Boolean = labelIndexes.isNotEmpty() || greedyLabel != null /** Returns the type of the `MakeFmt`. */ - fun type(): Writable = if (hasRedactions()) when (this) { - is Normal -> writable { - rustTemplate("#{SmithyHttpServer}::logging::sensitivity::uri::MakeLabel bool>", *codegenScope) - } - is Greedy -> writable { - rustTemplate("#{SmithyHttpServer}::logging::sensitivity::uri::MakeGreedyLabel", *codegenScope) - } + fun type(): Writable = if (hasRedactions()) writable { + rustTemplate("#{SmithyHttpServer}::logging::sensitivity::uri::MakeLabel bool>", *codegenScope) } else writable { rustTemplate("#{SmithyHttpServer}::logging::MakeIdentity", *codegenScope) } - /** Returns the setter enclosing the closure or suffix position. */ - fun setter(): Writable = if (hasRedactions()) when (this) { - is Normal -> writable { - rustTemplate(".label(#{Closure:W})", "Closure" to closure()) - } - is Greedy -> { - val suffixPosition = suffixPosition - writable { - rust(".greedy_label($suffixPosition)") - } + /** Returns the value of the `GreedyLabel`. */ + private fun greedyLabelStruct(): Writable = writable { + if (greedyLabel != null) { + rustTemplate( + """ + Some(#{SmithyHttpServer}::logging::sensitivity::uri::GreedyLabel::new(${greedyLabel.index}, "${greedyLabel.suffix}"))""", + *codegenScope, + ) + } else { + rust("None") } - } else writable {} + } + + /** Returns the setter enclosing the closure or suffix position. */ + fun setter(): Writable = if (hasRedactions()) writable { + rustTemplate(".label(#{Closure:W}, #{GreedyLabel:W})", "Closure" to closure(), "GreedyLabel" to greedyLabelStruct()) + } else writable { } } /** Models the ways headers can be bound and sensitive */ @@ -384,8 +387,10 @@ class ServerHttpSensitivityGenerator( // All values are sensitive HeaderSensitivity.SensitiveMapValue(headerKeys, keySensitive, httpPrefixName, runtimeConfig) } else if (keySensitive) { + // Only keys are sensitive HeaderSensitivity.NotSensitiveMapValue(headerKeys, httpPrefixName, runtimeConfig) } else { + // No values are sensitive HeaderSensitivity.NotSensitiveMapValue(headerKeys, null, runtimeConfig) } } @@ -432,9 +437,11 @@ class ServerHttpSensitivityGenerator( } /** Constructs `LabelSensitivity` of a `Shape` */ - internal fun findLabelSensitivity(uriPattern: UriPattern, rootShape: Shape): LabelSensitivity { - return findUriGreedyLabelPosition(uriPattern)?.let { LabelSensitivity.Greedy(it, runtimeConfig) } ?: findUriLabelIndexes(uriPattern, rootShape).let { LabelSensitivity.Normal(it, runtimeConfig) } - } + internal fun findLabelSensitivity(uriPattern: UriPattern, rootShape: Shape): LabelSensitivity = LabelSensitivity( + findUriLabelIndexes(uriPattern, rootShape), + findGreedyLabel(uriPattern), + runtimeConfig, + ) // Find member shapes with trait `B` contained in a shape enjoying `A`. // [trait|A] ~> [trait|B] diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt index d38a60804f..6ec493790f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGeneratorTest.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import io.kotest.assertions.fail import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import software.amazon.smithy.model.pattern.UriPattern @@ -34,11 +33,19 @@ class ServerHttpSensitivityGeneratorTest { ) @Test - fun `find greedy label`() { + fun `find greedy label end`() { val uri = "/pokemon-species/{name+}" val pattern = UriPattern.parse(uri) - val position = findUriGreedyLabelPosition(pattern)!! - assertEquals(position, 17) + val value = findGreedyLabel(pattern)!! + assertEquals(value, GreedyLabel(1, "")) + } + + @Test + fun `find greedy label`() { + val uri = "/pokemon-species/{name+}/ash/ketchum" + val pattern = UriPattern.parse(uri) + val value = findGreedyLabel(pattern)!! + assertEquals(value, GreedyLabel(1, "/ash/ketchum")) } @Test @@ -591,26 +598,22 @@ class ServerHttpSensitivityGeneratorTest { val labeledUriIndexes = generator.findUriLabelIndexes(uri, input) assertEquals(labeledUriIndexes, listOf(2, 1)) - when (val labelData = generator.findLabelSensitivity(uri, input)) { - is LabelSensitivity.Greedy -> fail("expected normal http label") - is LabelSensitivity.Normal -> { - val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) - testProject.lib { writer -> - writer.unitTest("uri_closure") { - rustTemplate( - """ - let closure = #{Closure:W}; - assert_eq!(closure(0), false); - assert_eq!(closure(1), true); - assert_eq!(closure(2), true); - """, - "Closure" to labelData.closure(), - *codegenScope, - ) - } - } - testProject.compileAndTest() + val labelData = generator.findLabelSensitivity(uri, input) + val testProject = TestWorkspace.testProject(serverTestSymbolProvider(model)) + testProject.lib { writer -> + writer.unitTest("uri_closure") { + rustTemplate( + """ + let closure = #{Closure:W}; + assert_eq!(closure(0), false); + assert_eq!(closure(1), true); + assert_eq!(closure(2), true); + """, + "Closure" to labelData.closure(), + *codegenScope, + ) } } + testProject.compileAndTest() } } diff --git a/rust-runtime/aws-smithy-http-server/src/logging/mod.rs b/rust-runtime/aws-smithy-http-server/src/logging/mod.rs index 26537c434c..99f6f94869 100644 --- a/rust-runtime/aws-smithy-http-server/src/logging/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/logging/mod.rs @@ -31,7 +31,7 @@ //! key_suffix: None, //! }) //! .query(|name| QueryMarker { key: false, value: name == "bar" }) -//! .label(|index| index % 2 == 0); +//! .label(|index| index % 2 == 0, None); //! let response_fmt = ResponseFmt::new() //! .header(|name| { //! if name.as_str().starts_with("prefix-") { diff --git a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/request.rs b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/request.rs index 012e601200..f2d3321b97 100644 --- a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/request.rs +++ b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/request.rs @@ -13,7 +13,7 @@ use crate::logging::{MakeFmt, MakeIdentity}; use super::{ headers::{HeaderMarker, MakeHeaders}, - uri::{MakeGreedyLabel, MakeLabel, MakeQuery, MakeUri, QueryMarker}, + uri::{GreedyLabel, MakeLabel, MakeQuery, MakeUri, QueryMarker}, }; /// Allows the modification the requests URIs [`Display`](std::fmt::Display) and headers @@ -70,27 +70,21 @@ impl RequestFmt> { /// Marks parts of the URI as sensitive. /// /// See [`Label`](super::uri::Label) for more info. - pub fn label(self, label: F) -> RequestFmt, Q>> + pub fn label( + self, + label_marker: F, + greedy_label: Option, + ) -> RequestFmt, Q>> where F: Fn(usize) -> bool, { RequestFmt { headers: self.headers, uri: MakeUri { - make_path: MakeLabel(label), - make_query: self.uri.make_query, - }, - } - } - - /// Marks parts of the URI as sensitive. - /// - /// See [`GreedyLabel`](super::uri::GreedyLabel) for more info. - pub fn greedy_label(self, position: usize) -> RequestFmt> { - RequestFmt { - headers: self.headers, - uri: MakeUri { - make_path: MakeGreedyLabel(position), + make_path: MakeLabel { + label_marker, + greedy_label, + }, make_query: self.uri.make_query, }, } diff --git a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/greedy_label.rs b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/greedy_label.rs deleted file mode 100644 index 918405e1e5..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/greedy_label.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! A wrapper around a path [`&str`](str) to allow for sensitivity. - -use std::fmt::{Debug, Display, Error, Formatter}; - -use crate::logging::{sensitivity::Sensitive, MakeFmt}; - -/// A wrapper around a path [`&str`](str) which modifies the behavior of [`Display`]. The suffix of the path is marked -/// as sensitive by providing a byte position. This accommodates the [httpLabel trait with greedy labels]. -/// -/// The [`Display`] implementation will respect the `unredacted-logging` flag. -/// -/// # Example -/// -/// ``` -/// # use aws_smithy_http_server::logging::sensitivity::uri::GreedyLabel; -/// # use http::Uri; -/// # let uri = ""; -/// // Everything after the 3rd byte is redacted -/// let uri = GreedyLabel::new(&uri, 3); -/// println!("{uri}"); -/// ``` -/// -/// [httpLabel trait with greedy labels]: https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html#greedy-labels -#[allow(missing_debug_implementations)] -pub struct GreedyLabel<'a> { - path: &'a str, - position: usize, -} - -impl<'a> GreedyLabel<'a> { - /// Constructs a new [`GreedyLabel`]. - pub fn new(path: &'a str, position: usize) -> Self { - Self { path, position } - } -} - -impl<'a> Display for GreedyLabel<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { - if self.path.len() < self.position { - Display::fmt(self.path, f)?; - } else { - write!(f, "{}", &self.path[..self.position])?; - write!(f, "{}", Sensitive(&self.path[self.position..]))?; - } - - Ok(()) - } -} - -/// A [`MakeFmt`] producing [`GreedyLabel`]. -#[derive(Debug, Clone)] -pub struct MakeGreedyLabel(pub(crate) usize); - -impl<'a> MakeFmt<&'a str> for MakeGreedyLabel { - type Target = GreedyLabel<'a>; - - fn make(&self, path: &'a str) -> Self::Target { - GreedyLabel::new(path, self.0) - } -} - -#[cfg(test)] -mod tests { - use http::Uri; - - use crate::logging::sensitivity::uri::tests::EXAMPLES; - - use super::*; - - #[cfg(not(feature = "unredacted-logging"))] - pub const GREEDY_EXAMPLES: [&str; 22] = [ - "g:h", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/g", - "http://g", - "http://a/b/{redacted}?y", - "http://a/b/{redacted}?y", - "http://a/b/{redacted}?q#s", - "http://a/b/{redacted}", - "http://a/b/{redacted}?y#s", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/b/{redacted}?y#s", - "http://a/b/{redacted}?q", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/", - "http://a/", - "http://a/g", - ]; - - #[cfg(feature = "unredacted-logging")] - pub const GREEDY_EXAMPLES: [&str; 22] = EXAMPLES; - - #[test] - fn greedy() { - let originals = EXAMPLES.into_iter().map(Uri::from_static); - let expecteds = GREEDY_EXAMPLES.into_iter().map(Uri::from_static); - for (original, expected) in originals.zip(expecteds) { - let output = GreedyLabel::new(&original.path(), 3).to_string(); - assert_eq!(output, expected.path(), "original = {original}"); - } - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/label.rs b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/label.rs index 5984158fcd..cd48908ae1 100644 --- a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/label.rs +++ b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/label.rs @@ -21,8 +21,8 @@ use crate::logging::{sensitivity::Sensitive, MakeFmt}; /// # use aws_smithy_http_server::logging::sensitivity::uri::Label; /// # use http::Uri; /// # let path = ""; -/// // Path segment 2 is redacted -/// let uri = Label::new(&path, |x| x == 2); +/// // Path segment 2 is redacted and a trailing greedy label +/// let uri = Label::new(&path, |x| x == 2, None); /// println!("{uri}"); /// ``` /// @@ -31,13 +31,44 @@ use crate::logging::{sensitivity::Sensitive, MakeFmt}; #[derive(Clone)] pub struct Label<'a, F> { path: &'a str, - marker: F, + label_marker: F, + greedy_label: Option, +} + +/// Marks a segment as a greedy label up until a char offset from the end. +/// +/// # Example +/// +/// The pattern, `/alpha/beta/{greedy+}/trail`, has segment index 2 and offset from the end of 6. +/// +/// ```rust +/// # use aws_smithy_http_server::logging::sensitivity::uri::GreedyLabel; +/// let greedy_label = GreedyLabel::new(2, 6); +/// ``` +#[derive(Clone, Debug)] +pub struct GreedyLabel { + segment_index: usize, + end_offset: usize, +} + +impl GreedyLabel { + /// Constructs a new [`GreedyLabel`] from a segment index and an offset from the end of the URI. + pub fn new(segment_index: usize, end_offset: usize) -> Self { + Self { + segment_index, + end_offset, + } + } } impl<'a, F> Label<'a, F> { /// Constructs a new [`Label`]. - pub fn new(path: &'a str, marker: F) -> Self { - Self { path, marker } + pub fn new(path: &'a str, label_marker: F, greedy_label: Option) -> Self { + Self { + path, + label_marker, + greedy_label, + } } } @@ -47,11 +78,47 @@ where { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { - for (index, segment) in self.path.split('/').skip(1).enumerate() { - if (self.marker)(index) { - write!(f, "/{}", Sensitive(segment))?; - } else { - write!(f, "/{}", segment)?; + if let Some(greedy_label) = &self.greedy_label { + let (greedy_start, hit_greedy) = self + .path + .split('/') + .skip(1) + .take(greedy_label.segment_index + 1) + .enumerate() + .fold(Ok((0, false)), |acc, (index, segment)| { + if index == greedy_label.segment_index { + // Greedy label exists. + Ok((acc?.0, true)) + } else { + // Prior to greedy segment, use label_marker and increment `greedy_start`. + if (self.label_marker)(index) { + write!(f, "/{}", Sensitive(segment))?; + } else { + write!(f, "/{}", segment)?; + } + Ok((acc?.0 + segment.len() + 1, false)) + } + })?; + + if hit_greedy { + if let Some(end_index) = self.path.len().checked_sub(greedy_label.end_offset) { + if greedy_start + 1 <= end_index { + let greedy_redaction = Sensitive(&self.path[greedy_start + 1..end_index]); + let remainder = &self.path[end_index..]; + println!("GREEDY: {greedy_redaction} vs {}", &self.path[greedy_start..end_index]); + write!(f, "/{greedy_redaction}{remainder}")?; + } else { + write!(f, "{}", &self.path[greedy_start..])?; + } + } + } + } else { + for (index, segment) in self.path.split('/').skip(1).enumerate() { + if (self.label_marker)(index) { + write!(f, "/{}", Sensitive(segment))?; + } else { + write!(f, "/{}", segment)?; + } } } @@ -61,11 +128,16 @@ where /// A [`MakeFmt`] producing [`Label`]. #[derive(Clone)] -pub struct MakeLabel(pub(crate) F); +pub struct MakeLabel { + pub(crate) label_marker: F, + pub(crate) greedy_label: Option, +} impl Debug for MakeLabel { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { - f.debug_tuple("MakeLabel").field(&"...").finish() + f.debug_struct("MakeLabel") + .field("greedy_label", &self.greedy_label) + .finish_non_exhaustive() } } @@ -76,7 +148,7 @@ where type Target = Label<'a, F>; fn make(&self, path: &'a str) -> Self::Target { - Label::new(path, self.0.clone()) + Label::new(path, self.label_marker.clone(), self.greedy_label.clone()) } } @@ -84,7 +156,7 @@ where mod tests { use http::Uri; - use crate::logging::sensitivity::uri::tests::EXAMPLES; + use crate::logging::sensitivity::uri::{tests::EXAMPLES, GreedyLabel}; use super::Label; @@ -93,13 +165,13 @@ mod tests { let originals = EXAMPLES.into_iter().map(Uri::from_static); for original in originals { let expected = original.path().to_string(); - let output = Label::new(&original.path(), |_| false).to_string(); + let output = Label::new(&original.path(), |_| false, None).to_string(); assert_eq!(output, expected, "original = {original}"); } } #[cfg(not(feature = "unredacted-logging"))] - const ALL_EXAMPLES: [&str; 22] = [ + const ALL_EXAMPLES: [&str; 19] = [ "g:h", "http://a/{redacted}/{redacted}/{redacted}", "http://a/{redacted}/{redacted}/{redacted}/{redacted}", @@ -118,21 +190,90 @@ mod tests { "http://a/{redacted}/{redacted}/{redacted}", "http://a/{redacted}/{redacted}", "http://a/{redacted}/{redacted}", - "http://a/{redacted}/{redacted}", - "http://a/{redacted}", - "http://a/{redacted}", "http://a/{redacted}", ]; #[cfg(feature = "unredacted-logging")] - pub const ALL_EXAMPLES: [&str; 22] = EXAMPLES; + pub const ALL_EXAMPLES: [&str; 19] = EXAMPLES; #[test] fn mark_all() { let originals = EXAMPLES.into_iter().map(Uri::from_static); let expecteds = ALL_EXAMPLES.into_iter().map(Uri::from_static); for (original, expected) in originals.zip(expecteds) { - let output = Label::new(&original.path(), |_| true).to_string(); + let output = Label::new(&original.path(), |_| true, None).to_string(); + assert_eq!(output, expected.path(), "original = {original}"); + } + } + + #[cfg(not(feature = "unredacted-logging"))] + pub const GREEDY_EXAMPLES: [&str; 19] = [ + "g:h", + "http://a/b/{redacted}", + "http://a/b/{redacted}", + "http://a/g", + "http://g", + "http://a/b/{redacted}?y", + "http://a/b/{redacted}?y", + "http://a/b/{redacted}?q#s", + "http://a/b/{redacted}", + "http://a/b/{redacted}?y#s", + "http://a/b/{redacted}", + "http://a/b/{redacted}", + "http://a/b/{redacted}?y#s", + "http://a/b/{redacted}?q", + "http://a/b/{redacted}", + "http://a/b/{redacted}", + "http://a/b/{redacted}", + "http://a/b/{redacted}", + "http://a/", + ]; + + #[cfg(feature = "unredacted-logging")] + pub const GREEDY_EXAMPLES: [&str; 19] = EXAMPLES; + + #[test] + fn greedy() { + let originals = EXAMPLES.into_iter().map(Uri::from_static); + let expecteds = GREEDY_EXAMPLES.into_iter().map(Uri::from_static); + for (original, expected) in originals.zip(expecteds) { + let output = Label::new(&original.path(), |_| false, Some(GreedyLabel::new(1, 0))).to_string(); + assert_eq!(output, expected.path(), "original = {original}"); + } + } + + #[cfg(not(feature = "unredacted-logging"))] + pub const GREEDY_EXAMPLES_OFFSET: [&str; 19] = [ + "g:h", + "http://a/b/{redacted}g", + "http://a/b/{redacted}/", + "http://a/g", + "http://g", + "http://a/b/{redacted}p?y", + "http://a/b/{redacted}g?y", + "http://a/b/{redacted}p?q#s", + "http://a/b/{redacted}g", + "http://a/b/{redacted}g?y#s", + "http://a/b/{redacted}x", + "http://a/b/{redacted}x", + "http://a/b/{redacted}x?y#s", + "http://a/b/{redacted}p?q", + "http://a/b/{redacted}/", + "http://a/b/{redacted}/", + "http://a/b/", + "http://a/b/{redacted}g", + "http://a/", + ]; + + #[cfg(feature = "unredacted-logging")] + pub const GREEDY_EXAMPLES_OFFSET: [&str; 19] = EXAMPLES; + + #[test] + fn greedy_offset() { + let originals = EXAMPLES.into_iter().map(Uri::from_static); + let expecteds = GREEDY_EXAMPLES_OFFSET.into_iter().map(Uri::from_static); + for (original, expected) in originals.zip(expecteds) { + let output = Label::new(&original.path(), |_| false, Some(GreedyLabel::new(1, 1))).to_string(); assert_eq!(output, expected.path(), "original = {original}"); } } diff --git a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/mod.rs b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/mod.rs index 8bbc6989c5..8438b1492a 100644 --- a/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/logging/sensitivity/uri/mod.rs @@ -5,7 +5,6 @@ //! Wrappers around [`Uri`] and it's constituents to allow for sensitivity. -mod greedy_label; mod label; mod query; @@ -13,7 +12,6 @@ use std::fmt::{Debug, Display, Error, Formatter}; use http::Uri; -pub use greedy_label::*; pub use label::*; pub use query::*; @@ -61,15 +59,11 @@ impl<'a, P, Q> SensitiveUri<'a, P, Q> { /// Marks path segments as sensitive by providing predicate over the segment index. /// /// See [`Label`] for more info. - pub fn label(self, marker: F) -> SensitiveUri<'a, MakeLabel, Q> { - self.make_path(MakeLabel(marker)) - } - - /// Marks the suffix of the path as sensitive by providing a byte position. - /// - /// See [`GreedyLabel`] for more info. - pub fn greedy_label(self, position: usize) -> SensitiveUri<'a, MakeGreedyLabel, Q> { - self.make_path(MakeGreedyLabel(position)) + pub fn label(self, label_marker: F, greedy_label: Option) -> SensitiveUri<'a, MakeLabel, Q> { + self.make_path(MakeLabel { + label_marker, + greedy_label, + }) } /// Marks specific query string values as sensitive by supplying a predicate over the query string keys. @@ -151,7 +145,7 @@ mod tests { // https://www.w3.org/2004/04/uri-rel-test.html // NOTE: http::Uri's `Display` implementation trims the fragment, we mirror this behavior - pub const EXAMPLES: [&str; 22] = [ + pub const EXAMPLES: [&str; 19] = [ "g:h", "http://a/b/c/g", "http://a/b/c/g/", @@ -169,11 +163,8 @@ mod tests { "http://a/b/c/", "http://a/b/c/", "http://a/b/", - "http://a/b/", "http://a/b/g", "http://a/", - "http://a/", - "http://a/g", ]; pub const QUERY_STRING_EXAMPLES: [&str; 11] = [ @@ -200,7 +191,7 @@ mod tests { } #[cfg(not(feature = "unredacted-logging"))] - const FIRST_PATH_EXAMPLES: [&str; 22] = [ + const FIRST_PATH_EXAMPLES: [&str; 19] = [ "g:h", "http://a/{redacted}/c/g", "http://a/{redacted}/c/g/", @@ -218,27 +209,24 @@ mod tests { "http://a/{redacted}/c/", "http://a/{redacted}/c/", "http://a/{redacted}/", - "http://a/{redacted}/", "http://a/{redacted}/g", "http://a/{redacted}", - "http://a/{redacted}", - "http://a/{redacted}", ]; #[cfg(feature = "unredacted-logging")] - const FIRST_PATH_EXAMPLES: [&str; 22] = EXAMPLES; + const FIRST_PATH_EXAMPLES: [&str; 19] = EXAMPLES; #[test] fn path_mark_first_segment() { let originals = EXAMPLES.into_iter().map(Uri::from_static); let expecteds = FIRST_PATH_EXAMPLES.into_iter().map(Uri::from_static); for (original, expected) in originals.zip(expecteds) { - let output = SensitiveUri::new(&original).label(|x| x == 0).to_string(); + let output = SensitiveUri::new(&original).label(|x| x == 0, None).to_string(); assert_eq!(output, expected.to_string(), "original = {original}"); } } #[cfg(not(feature = "unredacted-logging"))] - const LAST_PATH_EXAMPLES: [&str; 22] = [ + const LAST_PATH_EXAMPLES: [&str; 19] = [ "g:h", "http://a/b/c/{redacted}", "http://a/b/c/g/{redacted}", @@ -257,13 +245,10 @@ mod tests { "http://a/b/c/{redacted}", "http://a/b/{redacted}", "http://a/b/{redacted}", - "http://a/b/{redacted}", - "http://a/{redacted}", - "http://a/{redacted}", "http://a/{redacted}", ]; #[cfg(feature = "unredacted-logging")] - const LAST_PATH_EXAMPLES: [&str; 22] = EXAMPLES; + const LAST_PATH_EXAMPLES: [&str; 19] = EXAMPLES; #[test] fn path_mark_last_segment() { @@ -271,7 +256,9 @@ mod tests { let expecteds = LAST_PATH_EXAMPLES.into_iter().map(Uri::from_static); for (original, expected) in originals.zip(expecteds) { let path_len = original.path().split('/').skip(1).count(); - let output = SensitiveUri::new(&original).label(|x| x + 1 == path_len).to_string(); + let output = SensitiveUri::new(&original) + .label(|x| x + 1 == path_len, None) + .to_string(); assert_eq!(output, expected.to_string(), "original = {original}"); } } diff --git a/rust-runtime/aws-smithy-http-server/src/logging/service.rs b/rust-runtime/aws-smithy-http-server/src/logging/service.rs index 191f61bad5..8a35e08e45 100644 --- a/rust-runtime/aws-smithy-http-server/src/logging/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/logging/service.rs @@ -92,7 +92,7 @@ where /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); /// let request_fmt = RequestFmt::new() -/// .label(|index| index == 1) +/// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); /// let mut svc = InstrumentOperation::new(svc, "foo-operation")