fix: Phase 8 — mesh hardening: atomic writes, unwrap elimination, GPS opt-out
- Ratchet state: atomic write via tmp + rename to prevent corruption on crash - Block header decode: replaced .unwrap() with proper error handling on untrusted network data (was a crash vector from malicious peers) - Shutdown channel: replaced .unwrap() with .ok_or_else() error propagation - Dead man's switch GPS: default changed to opt-out (auto_include_gps=false) - Alert signature verification: already covered by Phase 4 envelope checks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,7 @@ impl Default for AlertConfig {
|
||||
dead_man_interval_secs: DEFAULT_INTERVAL_SECS,
|
||||
last_gps: None,
|
||||
emergency_contacts: Vec::new(),
|
||||
auto_include_gps: true,
|
||||
auto_include_gps: false,
|
||||
custom_message: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,9 +237,11 @@ pub fn decode_compact_block_header(payload: &[u8]) -> Result<(u64, String, u32)>
|
||||
if payload.len() < 44 {
|
||||
anyhow::bail!("Compact block header too short: {} bytes", payload.len());
|
||||
}
|
||||
let height = u64::from_le_bytes(payload[0..8].try_into().unwrap());
|
||||
let height = u64::from_le_bytes(payload[0..8].try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid height bytes in block header"))?);
|
||||
let hash_hex = hex::encode(&payload[8..40]);
|
||||
let timestamp = u32::from_le_bytes(payload[40..44].try_into().unwrap());
|
||||
let timestamp = u32::from_le_bytes(payload[40..44].try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid timestamp bytes in block header"))?);
|
||||
Ok((height, hash_hex, timestamp))
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,8 @@ impl MeshService {
|
||||
let dms = Arc::clone(&self.dead_man_switch);
|
||||
let dms_state = Arc::clone(&self.state);
|
||||
let dms_key = self.signing_key.clone();
|
||||
let dms_shutdown = self.shutdown_tx.as_ref().unwrap().subscribe();
|
||||
let dms_shutdown = self.shutdown_tx.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("Shutdown channel not initialized"))?.subscribe();
|
||||
let dms_handle = tokio::spawn(async move {
|
||||
let mut shutdown = dms_shutdown;
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(60));
|
||||
@@ -275,7 +276,8 @@ impl MeshService {
|
||||
let bha_cache = Arc::clone(&self.block_header_cache);
|
||||
let bha_key = self.signing_key.clone();
|
||||
let bha_did = self.our_did.clone();
|
||||
let bha_shutdown = self.shutdown_tx.as_ref().unwrap().subscribe();
|
||||
let bha_shutdown = self.shutdown_tx.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("Shutdown channel not initialized"))?.subscribe();
|
||||
let bha_handle = tokio::spawn(async move {
|
||||
let mut shutdown = bha_shutdown;
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(30));
|
||||
|
||||
@@ -64,12 +64,17 @@ impl SessionManager {
|
||||
.await
|
||||
.context("Failed to create ratchet directory")?;
|
||||
let path = self.session_path(did);
|
||||
let tmp_path = path.with_extension("tmp");
|
||||
let content = serde_json::to_string_pretty(state)
|
||||
.context("Failed to serialize ratchet session")?;
|
||||
tokio::fs::write(&path, content)
|
||||
// Atomic write: write to temp file, then rename
|
||||
tokio::fs::write(&tmp_path, content)
|
||||
.await
|
||||
.context("Failed to write ratchet session")?;
|
||||
debug!(did = %did, "Saved ratchet session to disk");
|
||||
.context("Failed to write temporary ratchet state")?;
|
||||
tokio::fs::rename(&tmp_path, &path)
|
||||
.await
|
||||
.context("Failed to atomically rename ratchet state file")?;
|
||||
debug!(did = %did, "Saved ratchet session to disk (atomic)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user