Files
chanora/poc/secure-storage-spike/src/test_logger.rs
T
EdisonJwa 50c95b61ad feat(poc/storage): add secure-storage spike (Linux)
Proof-of-concept proving the secure-storage exit criterion from
docs/architecture/proof-of-concept-plan.md §2:
  "Secret write/read/delete works through platform secure storage."

Implements a typed SecretStorageRepository trait per ADR-006
(SecureStore + per-platform adapters) and a Linux adapter (the only
adapter in PoC scope) that supports both equivalent Linux backends
per SysRS-053/SysRS-162: Secret Service (libsecret) and kernel
keyutils.

The audit test suite covers:
  SS-AUD-001  identity secret absent from local DB (raw file scan)
  SS-AUD-002  server password absent from local DB
  SS-AUD-003  secrets absent from logs (Secret newtype redaction)
  SS-AUD-005  failure returns safe typed error (NotFound)
  SS-AUD-006  delete removes entry
  SS-TC-003   Linux round-trip set/get/delete

Verified on 2026-05-13 against the local keyutils backend (cargo
test runs need 'keyctl session -' to provide a valid session
keyring under non-interactive shells, documented in the spike
README). The CLI driver additionally observed a real locked
gnome-keyring collection and exercised the typed-error → fallback
path live.

Surfaced finding for the decision register: DEC-013 does not pin
a Linux secure-storage backend policy. Both Secret Service and
keyutils are 'equivalent' per the requirements; production code
needs an owner ruling.

Out of scope: Windows DPAPI, macOS/iOS Keychain, Android Keystore,
SS-AUD-004 (covered by diagnostics-redaction spike), SS-AUD-007/008
(process / migration items).

Authority: PoC plan §2, ADR-006, SDD-078, SRS-091..095,
SysRS-158..162.
Not product code; not promoted into chanora_storage.
2026-05-14 12:26:23 +08:00

55 lines
1.5 KiB
Rust

//! In-memory log capture, used by the audit tests to assert that
//! `tracing` output contains no secret material (SS-AUD-003).
//!
//! Production code will use the redaction policy defined in
//! `docs/security/diagnostic-redaction-audit-report.md` and the
//! `chanora_diagnostics` crate. This PoC is intentionally simpler:
//! it captures *all* log output verbatim and lets the test grep it.
use std::io::{self, Write};
use std::sync::{Arc, Mutex};
#[derive(Clone, Default)]
pub struct CapturedLog {
buf: Arc<Mutex<Vec<u8>>>,
}
impl CapturedLog {
pub fn new() -> Self {
Self::default()
}
/// Snapshot the captured bytes as a UTF-8 string (lossy if there's
/// any non-UTF-8 — we never expect non-UTF-8 from `tracing`).
pub fn snapshot(&self) -> String {
let buf = self.buf.lock().unwrap();
String::from_utf8_lossy(&buf).into_owned()
}
pub fn writer(&self) -> CapturedLogWriter {
CapturedLogWriter { buf: self.buf.clone() }
}
}
pub struct CapturedLogWriter {
buf: Arc<Mutex<Vec<u8>>>,
}
impl Write for CapturedLogWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut g = self.buf.lock().unwrap();
g.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for CapturedLog {
type Writer = CapturedLogWriter;
fn make_writer(&'a self) -> Self::Writer {
self.writer()
}
}