fix(security): Implement Phase 1 critical security fixes

CORS:
- Restrict CORS to DASHBOARD_URL environment variable
- Default to production dashboard domain

Authentication:
- Add AuthUser requirement to all agent management endpoints
- Add AuthUser requirement to all command endpoints
- Add AuthUser requirement to all metrics endpoints
- Add audit logging for command execution (user_id tracked)

Agent Security:
- Replace Unicode characters with ASCII markers [OK]/[ERROR]/[WARNING]
- Add certificate pinning for update downloads (allowlist domains)
- Fix insecure temp file creation (use /var/run/gururmm with 0700 perms)
- Fix rollback script backgrounding (use setsid instead of literal &)

Dashboard Security:
- Move token storage from localStorage to sessionStorage
- Add proper TypeScript types (remove 'any' from error handlers)
- Centralize token management functions

Legacy Agent:
- Add -AllowInsecureTLS parameter (opt-in required)
- Add Windows Event Log audit trail when insecure mode used
- Update documentation with security warnings

Closes: Phase 1 items in issue #1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 21:16:24 -07:00
parent 6d3271c144
commit 65086f4407
15 changed files with 1708 additions and 99 deletions

View File

@@ -5,10 +5,11 @@ use axum::{
http::StatusCode,
Json,
};
use chrono::{DateTime, Duration, Utc};
use chrono::{DateTime, Utc};
use serde::Deserialize;
use uuid::Uuid;
use crate::auth::AuthUser;
use crate::db::{self, Metrics, MetricsSummary};
use crate::AppState;
@@ -26,13 +27,15 @@ pub struct MetricsQuery {
}
/// Get metrics for a specific agent
/// Requires authentication.
pub async fn get_agent_metrics(
State(state): State<AppState>,
_user: AuthUser,
Path(id): Path<Uuid>,
Query(query): Query<MetricsQuery>,
) -> Result<Json<Vec<Metrics>>, (StatusCode, String)> {
// First verify the agent exists
let agent = db::get_agent_by_id(&state.db, id)
let _agent = db::get_agent_by_id(&state.db, id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Agent not found".to_string()))?;
@@ -54,8 +57,10 @@ pub async fn get_agent_metrics(
}
/// Get summary metrics across all agents
/// Requires authentication.
pub async fn get_summary(
State(state): State<AppState>,
_user: AuthUser,
) -> Result<Json<MetricsSummary>, (StatusCode, String)> {
let summary = db::get_metrics_summary(&state.db)
.await