Harden DetMir DLP production runtime
CI / Rust checks (push) Canceled after 0s
CI / Docs and registry checks (push) Canceled after 0s
CI / Smoke checks (push) Canceled after 0s
Coverage / Coverage baseline (push) Canceled after 0s
Security / Cargo audit (push) Canceled after 0s
Security / Cargo deny (push) Canceled after 0s
Security / Secret pattern check (push) Canceled after 0s
Security / Dependency review (push) Canceled after 0s

- default DetMir DLP runtime to core_only/disabled with load-guard protection

- add fail-closed placeholder validation and runtime-scoped artifact checks

- document operator re-enable flow for light profile and guard rollback

- update prod docs, env examples, and Ansible DLP defaults
This commit is contained in:
igor04091968
2026-07-01 00:05:23 +03:00
parent 1149f5dfbd
commit fe87c85a31
26 changed files with 3053 additions and 220 deletions
@@ -6,13 +6,20 @@
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use crate::{Cli, HealthResponse, Snapshot, build_health, build_snapshot, now};
const SNAPSHOT_CACHE_TTL: Duration = Duration::from_secs(120);
pub(crate) type SnapshotCache = Arc<Mutex<Option<CachedSnapshot>>>;
pub(crate) type SnapshotCache = Arc<Mutex<SnapshotCacheState>>;
#[derive(Clone, Debug, Default)]
pub(crate) struct SnapshotCacheState {
pub(crate) entry: Option<CachedSnapshot>,
pub(crate) refresh_in_progress: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct CachedSnapshot {
@@ -21,7 +28,7 @@ pub(crate) struct CachedSnapshot {
}
pub(crate) fn new_snapshot_cache() -> SnapshotCache {
Arc::new(Mutex::new(None))
Arc::new(Mutex::new(SnapshotCacheState::default()))
}
pub(crate) fn clone_snapshot_cache(cache: &SnapshotCache) -> SnapshotCache {
@@ -29,23 +36,76 @@ pub(crate) fn clone_snapshot_cache(cache: &SnapshotCache) -> SnapshotCache {
}
pub(crate) fn cached_snapshot(args: &Cli, cache: &SnapshotCache) -> Snapshot {
let mut guard = cache.lock().expect("snapshot cache mutex poisoned");
if let Some(cached) = guard.as_ref() {
if cached.created.elapsed() <= SNAPSHOT_CACHE_TTL {
return cached.snapshot.clone();
{
let guard = cache.lock().expect("snapshot cache mutex poisoned");
if let Some(cached) = guard.entry.as_ref() {
if cached.created.elapsed() <= SNAPSHOT_CACHE_TTL {
return cached.snapshot.clone();
}
}
}
let snapshot = build_snapshot(args);
*guard = Some(CachedSnapshot {
let mut guard = cache.lock().expect("snapshot cache mutex poisoned");
guard.entry = Some(CachedSnapshot {
created: Instant::now(),
snapshot: snapshot.clone(),
});
guard.refresh_in_progress = false;
snapshot
}
pub(crate) fn cached_snapshot_or_refresh(args: &Cli, cache: &SnapshotCache) -> Option<Snapshot> {
let mut should_spawn = false;
let mut snapshot_to_return = None;
{
let mut guard = cache.lock().expect("snapshot cache mutex poisoned");
if let Some(cached) = guard.entry.as_ref() {
let snapshot = cached.snapshot.clone();
if cached.created.elapsed() <= SNAPSHOT_CACHE_TTL {
return Some(snapshot);
}
if !guard.refresh_in_progress {
guard.refresh_in_progress = true;
should_spawn = true;
}
snapshot_to_return = Some(snapshot);
} else if !guard.refresh_in_progress {
guard.refresh_in_progress = true;
should_spawn = true;
}
}
if should_spawn {
spawn_snapshot_refresh(args.clone(), clone_snapshot_cache(cache));
}
snapshot_to_return
}
fn spawn_snapshot_refresh(args: Cli, cache: SnapshotCache) {
thread::spawn(move || {
let result =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| build_snapshot(&args)));
let mut guard = cache.lock().expect("snapshot cache mutex poisoned");
match result {
Ok(snapshot) => {
guard.entry = Some(CachedSnapshot {
created: Instant::now(),
snapshot,
});
}
Err(_) => {
eprintln!("detmir-portal snapshot cache refresh panicked");
}
}
guard.refresh_in_progress = false;
});
}
pub(crate) fn build_fast_health(cache: &SnapshotCache) -> HealthResponse {
match cache.try_lock() {
Ok(guard) => guard
.entry
.as_ref()
.map(|cached| build_health(&cached.snapshot))
.unwrap_or_else(lightweight_health),