Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions insta/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ pub fn serialize_content(mut content: Content, format: SerializationFormat) -> S
}
#[cfg(feature = "redactions")]
{
for (selector, redaction) in settings.iter_redactions() {
content = selector.redact(content, redaction);
}
content = settings.apply_redactions(content);
}
content
});
Expand Down
33 changes: 30 additions & 3 deletions insta/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ impl<'a> From<Vec<(&'a str, Redaction)>> for Redactions {
}
}

#[cfg(feature = "redactions")]
impl Redactions {
/// Applies all redactions to the given content.
pub(crate) fn apply_to_content(&self, mut content: Content) -> Content {
for (selector, redaction) in self.0.iter() {
content = selector.redact(content, redaction);
}
content
}
}

#[derive(Clone)]
#[doc(hidden)]
pub struct ActualSettings {
Expand Down Expand Up @@ -100,6 +111,15 @@ impl ActualSettings {
pub fn info<S: Serialize>(&mut self, s: &S) {
let serializer = ContentSerializer::<ValueError>::new();
let content = Serialize::serialize(s, serializer).unwrap();

// Apply redactions to metadata immediately when set. Unlike snapshot
// content (which is redacted lazily during serialization), metadata is
// redacted eagerly to ensure sensitive data never reaches the stored
// settings. The redacted content is then written to the snapshot file
// as-is without further redaction.
#[cfg(feature = "redactions")]
let content = self.redactions.apply_to_content(content);

self.info = Some(content);
}

Expand Down Expand Up @@ -319,6 +339,9 @@ impl Settings {
/// As an example the input parameters to the function that creates the snapshot
/// can be persisted here.
///
/// **Note:** Redactions configured via [`Self::add_redaction`] are automatically
/// applied to the info metadata when it is set.
///
/// Alternatively you can use [`Self::set_raw_info`] instead.
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
Expand All @@ -329,6 +352,10 @@ impl Settings {
/// Sets the info from a content object.
///
/// This works like [`Self::set_info`] but does not require [`serde`].
///
/// **Note:** Unlike [`Self::set_info`], this method does NOT automatically apply
/// redactions. If you need redactions applied to metadata, use [`Self::set_info`]
/// instead (which requires the `serde` feature).
pub fn set_raw_info(&mut self, content: &Content) {
self._private_inner_mut().raw_info(content);
}
Expand Down Expand Up @@ -421,11 +448,11 @@ impl Settings {
self._private_inner_mut().redactions.0.clear();
}

/// Iterate over the redactions.
/// Apply redactions to content.
#[cfg(feature = "redactions")]
#[cfg_attr(docsrs, doc(cfg(feature = "redactions")))]
pub(crate) fn iter_redactions(&self) -> impl Iterator<Item = (&Selector<'_>, &Redaction)> {
self.inner.redactions.0.iter().map(|(a, b)| (a, &**b))
pub(crate) fn apply_redactions(&self, content: Content) -> Content {
self.inner.redactions.apply_to_content(content)
}

/// Adds a new filter.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: insta/tests/test_redaction.rs
expression: "&vec![1, 2, 3]"
info:
secret: sensitive_value
public: visible
---
- 1
- 2
- 3
10 changes: 10 additions & 0 deletions insta/tests/snapshots/test_redaction__metadata_redaction_test.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: insta/tests/test_redaction.rs
expression: "&vec![1, 2, 3]"
info:
secret: "[REDACTED]"
public: visible
---
- 1
- 2
- 3
42 changes: 42 additions & 0 deletions insta/tests/test_redaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,3 +608,45 @@ fn test_named_redacted_supported_form() {
}
);
}

#[cfg(all(feature = "yaml", feature = "redactions"))]
#[test]
fn test_metadata_redaction() {
#[derive(Serialize)]
struct Info {
secret: String,
public: String,
}

let mut settings = insta::Settings::new();
settings.add_redaction(".secret", "[REDACTED]");
settings.set_info(&Info {
secret: "sensitive_value".into(),
public: "visible".into(),
});

settings.bind(|| {
assert_yaml_snapshot!("metadata_redaction_test", &vec![1, 2, 3]);
});
}

#[cfg(all(feature = "yaml", feature = "redactions"))]
#[test]
fn test_metadata_raw_info_no_redaction() {
use insta::internals::Content;

let mut settings = insta::Settings::new();
settings.add_redaction(".secret", "[REDACTED]");

// Create content that would be redacted if redactions were applied
let content = Content::Map(vec![
(Content::from("secret"), Content::from("sensitive_value")),
(Content::from("public"), Content::from("visible")),
]);

settings.set_raw_info(&content);

settings.bind(|| {
assert_yaml_snapshot!("metadata_raw_info_no_redaction", &vec![1, 2, 3]);
});
}
Loading