mirror of
https://github.com/ruvnet/RuView
synced 2026-08-04 19:31:42 +00:00
fix(homecore-api/sec): close HC-01/HC-02 — real bearer-token store
Replaces the P1 "any non-empty bearer" placeholder with a real LongLivedTokenStore (HashSet<String>) on SharedState. Closes the two Critical findings from the iter-10 security audit (docs/security/HOMECORE-security-audit-iter10.md HC-01 + HC-02). New module `homecore-api::tokens`: - LongLivedTokenStore::empty() — default-deny - LongLivedTokenStore::from_env() — reads HOMECORE_TOKENS=t1,t2,t3 - LongLivedTokenStore::allow_any_non_empty() — DEV-only, warns on every check, preserves legacy behaviour for migrating users - register / revoke / is_valid / len / is_dev_mode — full API Wired through: - SharedState gains `tokens: LongLivedTokenStore`; constructors with_tokens(...) for explicit injection; with_metadata defaults to DEV (allow_any) for backwards compat with existing smoke tests - BearerAuth::from_headers now async + takes &LongLivedTokenStore; checks store.is_valid(token) before returning Ok - All 6 REST handlers updated to thread the store and await the validation - homecore-server reads HOMECORE_TOKENS at boot; if set, builds the store from env; if unset, falls back to DEV with a warn log Test count: 4 → 15 (+11 token-store + auth-with-store tests). Smoke verified end-to-end: HOMECORE_TOKENS=good homecore-server --bind 127.0.0.1:8126 → "LongLivedTokenStore provisioned with 1 bearer token(s)" curl -H "Authorization: Bearer good" .../api/states → 200 curl -H "Authorization: Bearer wrong" .../api/states → 401 curl -H "Authorization: Bearer " .../api/states → 401 curl .../api/states → 401 Refs: docs/security/HOMECORE-security-audit-iter10.md (HC-01 + HC-02) Refs: docs/adr/ADR-130-homecore-rest-websocket-api.md §3 auth Refs: #798 Refs: #800 Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -26,7 +26,7 @@ use clap::Parser;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use homecore::HomeCore;
|
||||
use homecore_api::{router, SharedState};
|
||||
use homecore_api::{router, LongLivedTokenStore, SharedState};
|
||||
use homecore_assist::pipeline::default_pipeline;
|
||||
use homecore_assist::RegexIntentRecognizer;
|
||||
use homecore_automation::AutomationEngine;
|
||||
@@ -118,7 +118,25 @@ async fn main() -> Result<()> {
|
||||
let _ = hap_bridge;
|
||||
|
||||
// ── 7. REST + WS API ────────────────────────────────────────────
|
||||
let api_state = SharedState::with_metadata(hc.clone(), cli.location_name, env!("CARGO_PKG_VERSION"));
|
||||
// Token provisioning closes audit findings HC-01/HC-02. If
|
||||
// HOMECORE_TOKENS is set in the env, populate the store from
|
||||
// its comma-separated list. Otherwise fall back to DEV mode
|
||||
// (warn-on-each-request) so existing smoke tests still work.
|
||||
let tokens = if std::env::var("HOMECORE_TOKENS").map(|v| !v.trim().is_empty()).unwrap_or(false) {
|
||||
let s = LongLivedTokenStore::from_env();
|
||||
let n = s.len().await;
|
||||
info!("LongLivedTokenStore provisioned with {} bearer token(s) from HOMECORE_TOKENS", n);
|
||||
s
|
||||
} else {
|
||||
warn!("HOMECORE_TOKENS not set — token store in DEV mode (any non-empty bearer accepted). Provision real tokens before exposing to the network.");
|
||||
LongLivedTokenStore::allow_any_non_empty()
|
||||
};
|
||||
let api_state = SharedState::with_tokens(
|
||||
hc.clone(),
|
||||
cli.location_name,
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
tokens,
|
||||
);
|
||||
let app = router(api_state);
|
||||
let listener = tokio::net::TcpListener::bind(cli.bind).await?;
|
||||
info!("HOMECORE-API listening on http://{} (HA-compat /api + /api/websocket)", cli.bind);
|
||||
|
||||
Reference in New Issue
Block a user