security: Fix 10 vulnerabilities, remove 12 dead code instances

Critical fixes:
- Remove hardcoded admin/admin123 credentials from UserManager
- Enable JWT signature verification (was disabled for debugging)
- Redact secrets from /dev/config endpoint (was exposing os.environ)
- Remove hardcoded SSH admin/admin credentials from hardware service
- Add channel validation to prevent command injection in router interface

Rust fixes:
- Replace partial_cmp().unwrap() with .unwrap_or(Equal) to prevent
  NaN panics in 6 locations across core, signal, nn, mat crates
- Replace .expect()/.unwrap() with safe fallbacks in utils, csi_receiver
- Replace SystemTime unwrap with unwrap_or_default

Dead code removed:
- Duplicate imports (CORSMiddleware, os, Path, ABC, subprocess)
- Unused AdaptiveRateLimit/RateLimitStorage/RedisRateLimitStorage (~110 lines)
- Unused _log_authentication_event method
- Unused Confidence::new_unchecked in Rust
- Fix bare except: clause to except Exception:

https://claude.ai/code/session_01Ki7pvEZtJDvqJkmyn6B714
This commit is contained in:
Claude
2026-02-28 07:04:22 +00:00
parent ea452ba5fc
commit 7afdad0723
23 changed files with 81 additions and 192 deletions

View File

@@ -172,16 +172,6 @@ impl Confidence {
/// Creates a confidence value without validation (for internal use).
///
/// # Safety
///
/// The caller must ensure the value is in [0.0, 1.0].
#[must_use]
#[allow(dead_code)]
pub(crate) fn new_unchecked(value: f32) -> Self {
debug_assert!((0.0..=1.0).contains(&value));
Self(value)
}
/// Returns the raw confidence value.
#[must_use]
pub fn value(&self) -> f32 {
@@ -1009,7 +999,12 @@ impl PoseEstimate {
pub fn highest_confidence_person(&self) -> Option<&PersonPose> {
self.persons
.iter()
.max_by(|a, b| a.confidence.value().partial_cmp(&b.confidence.value()).unwrap())
.max_by(|a, b| {
a.confidence
.value()
.partial_cmp(&b.confidence.value())
.unwrap_or(std::cmp::Ordering::Equal)
})
}
}

View File

@@ -98,8 +98,11 @@ pub fn moving_average(data: &Array1<f64>, window_size: usize) -> Array1<f64> {
let mut result = Array1::zeros(data.len());
let half_window = window_size / 2;
// Safe unwrap: ndarray Array1 is always contiguous
let slice = data.as_slice().expect("Array1 should be contiguous");
// ndarray Array1 is always contiguous, but handle gracefully if not
let slice = match data.as_slice() {
Some(s) => s,
None => return data.clone(),
};
for i in 0..data.len() {
let start = i.saturating_sub(half_window);

View File

@@ -310,7 +310,8 @@ impl DisasterEvent {
// Create new survivor
let survivor = Survivor::new(zone_id, vitals, location);
self.survivors.push(survivor);
Ok(self.survivors.last().unwrap())
// Safe: we just pushed, so last() is always Some
Ok(self.survivors.last().expect("survivors is non-empty after push"))
}
/// Find a survivor near a location

View File

@@ -701,8 +701,14 @@ impl PcapCsiReader {
};
if pcap_config.playback_speed > 0.0 {
let packet_offset = packet.timestamp - self.start_time.unwrap();
let real_offset = Utc::now() - self.playback_time.unwrap();
let Some(start_time) = self.start_time else {
return Ok(None);
};
let Some(playback_time) = self.playback_time else {
return Ok(None);
};
let packet_offset = packet.timestamp - start_time;
let real_offset = Utc::now() - playback_time;
let scaled_offset = packet_offset
.num_milliseconds()
.checked_div((pcap_config.playback_speed * 1000.0) as i64)

View File

@@ -805,7 +805,7 @@ impl HardwareAdapter {
let num_subcarriers = config.channel_config.num_subcarriers;
let t = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.unwrap_or_default()
.as_secs_f64();
// Generate simulated breathing pattern (~0.3 Hz)
@@ -1102,7 +1102,7 @@ fn rand_simple() -> f64 {
use std::time::SystemTime;
let nanos = SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.unwrap_or_default()
.subsec_nanos();
(nanos % 1000) as f64 / 1000.0 - 0.5
}

View File

@@ -164,7 +164,7 @@ impl DebrisClassification {
pub fn new(probabilities: Vec<f32>) -> Self {
let (max_idx, &max_prob) = probabilities.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or((7, &0.0));
// Check for composite materials (multiple high probabilities)
@@ -216,7 +216,7 @@ impl DebrisClassification {
self.class_probabilities.iter()
.enumerate()
.filter(|(i, _)| *i != primary_idx)
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| MaterialType::from_index(i))
}
}

View File

@@ -593,7 +593,7 @@ impl VitalSignsClassifier {
.enumerate()
.skip(1) // Skip DC
.take(30) // Up to ~30% of Nyquist
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or((0, &0.0));
// Store dominant frequency in last position

View File

@@ -285,7 +285,7 @@ impl Tensor {
let result = a.map_axis(ndarray::Axis(axis), |row| {
row.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i as i64)
.unwrap_or(0)
});

View File

@@ -490,7 +490,9 @@ impl PowerSpectralDensity {
let peak_idx = positive_psd
.iter()
.enumerate()
.max_by(|(_, a): &(usize, &f64), (_, b): &(usize, &f64)| a.partial_cmp(b).unwrap())
.max_by(|(_, a): &(usize, &f64), (_, b): &(usize, &f64)| {
a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
})
.map(|(i, _)| i)
.unwrap_or(0);
let peak_frequency = positive_freq[peak_idx];