commit 8e4b381e7f9b3ae5e166fe20abaa7c3a57c8d61f
parent e082385ed7a2eb6bdd5e002fbfb6bee22407e58a
Author: triesap <tyson@radroots.org>
Date: Sun, 2 Aug 2026 18:51:19 +0000
test(core): add public redaction assertions
- scan public snapshot and safe-error debug representations
- inspect representative SQLite schema and durable records
- reject known secret hex nsec and secret-prefix fixtures
- document the reusable pre-command redaction guard lane
Diffstat:
2 files changed, 100 insertions(+), 0 deletions(-)
diff --git a/crates/studio_application/tests/redaction.rs b/crates/studio_application/tests/redaction.rs
@@ -0,0 +1,47 @@
+use radroots_studio_application::{
+ AppSnapshot, RelayConfiguration, SessionState, SnapshotRevision,
+};
+use radroots_studio_domain::{
+ AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SafeError, SafeErrorCode,
+ SafeMessage, SignerKind, UnixTimestamp,
+};
+
+const SECRET_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111";
+const SECRET_NSEC: &str = "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5";
+const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
+
+fn assert_redacted(text: &str) {
+ assert!(!text.contains(SECRET_HEX));
+ assert!(!text.contains(SECRET_NSEC));
+ assert!(!text.contains("nsec1"));
+}
+
+#[test]
+fn redaction_guards_public_snapshot_and_safe_error_debug() {
+ let account = AccountSummary::new(
+ PublicKey::from_bytes([2; 32]),
+ Npub::from_encoded(NPUB.to_owned()).expect("npub"),
+ SignerKind::LocalSecret,
+ KeyAvailability::Available,
+ None,
+ AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
+ None,
+ );
+ let snapshot = AppSnapshot::ready(
+ SnapshotRevision::from_value(1),
+ RelayConfiguration::default(),
+ vec![account.clone()],
+ Some(account.public_key()),
+ SessionState::SignedOut,
+ None,
+ None,
+ )
+ .expect("snapshot");
+ let error = SafeError::new(
+ SafeErrorCode::KeyringUnavailable,
+ SafeMessage::new("The operating system credential store is unavailable."),
+ );
+
+ assert_redacted(&format!("{snapshot:?}"));
+ assert_redacted(&format!("{error:?} {error}"));
+}
diff --git a/crates/studio_storage/tests/redaction.rs b/crates/studio_storage/tests/redaction.rs
@@ -0,0 +1,53 @@
+use std::fs;
+
+use radroots_studio_application::{AccountOperationKind, AccountRepository, OperationJournal};
+use radroots_studio_domain::{
+ AccountCreatedAt, AccountSummary, KeyAvailability, Npub, PublicKey, SignerKind, UnixTimestamp,
+};
+use radroots_studio_storage::Database;
+use tempfile::tempdir;
+
+const SECRET_HEX: &str = "1111111111111111111111111111111111111111111111111111111111111111";
+const SECRET_NSEC: &str = "nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5";
+const NPUB: &str = "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg";
+
+fn assert_redacted(bytes: &[u8]) {
+ assert!(
+ !bytes
+ .windows(SECRET_HEX.len())
+ .any(|value| value == SECRET_HEX.as_bytes())
+ );
+ assert!(
+ !bytes
+ .windows(SECRET_NSEC.len())
+ .any(|value| value == SECRET_NSEC.as_bytes())
+ );
+ assert!(!bytes.windows(5).any(|value| value == b"nsec1"));
+}
+
+#[test]
+fn redaction_guards_sqlite_schema_and_non_secret_records() {
+ let directory = tempdir().expect("directory");
+ let path = directory.path().join("studio.sqlite3");
+ {
+ let database = Database::open(&path).expect("database");
+ let account = AccountSummary::new(
+ PublicKey::from_bytes([2; 32]),
+ Npub::from_encoded(NPUB.to_owned()).expect("npub"),
+ SignerKind::LocalSecret,
+ KeyAvailability::Available,
+ None,
+ AccountCreatedAt::new(UnixTimestamp::from_seconds(1).expect("time")),
+ None,
+ );
+ database.insert_account(&account).expect("account");
+ database
+ .begin_operation(
+ AccountOperationKind::Add,
+ account.public_key(),
+ UnixTimestamp::from_seconds(2).expect("time"),
+ )
+ .expect("journal");
+ }
+ assert_redacted(&fs::read(path).expect("database bytes"));
+}