mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-31 16:49:34 +00:00
fix(security): make unsafe env::set_var calls safe with explicit invariants (#968)
* fix(security): make unsafe env::set_var calls safe with explicit invariants `std::env::set_var` is unsafe in Rust 1.82+ because concurrent calls from multiple threads cause undefined behavior. This commit addresses the two production-code call sites: 1. `bootstrap.rs:load_ironclaw_env()` -- called before the Tokio runtime starts (genuinely single-threaded). Added a `debug_assert!` that verifies no Tokio runtime is active, making the safety invariant machine-checkable rather than relying on a comment. 2. `llm/session.rs:api_key_login()` -- was calling `set_var` mid- execution inside the multi-threaded Tokio runtime (UB risk). Replaced with `set_runtime_env()`, a new thread-safe overlay backed by `OnceLock<Mutex<HashMap>>`. The overlay integrates with the existing `optional_env()` config resolution and a new `env_or_override()` reader function. All call sites that read `NEARAI_API_KEY` via raw `std::env::var()` (wizard.rs, main.rs, doctor.rs) are updated to use the thread-safe `env_or_override()` helper instead, so the value set during interactive login is visible without mutating the process environment. Test code `set_var`/`remove_var` calls (bootstrap tests, config tests, shell tests, oauth tests, wizard tests) are left as-is since they run under `ENV_MUTEX` serialization and are not production paths. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix: address review feedback on thread-safe env overlay PR - Replace debug_assert! with runtime check in bootstrap.rs so release builds skip unsafe set_var when a Tokio runtime is active - Recover from mutex poison in set_runtime_env instead of silently dropping writes (poisoned HashMap is still usable) - Skip empty override values in env_or_override and optional_env for consistency with real env var handling - Fix doc comment on env_or_override (real env checked first, not runtime overrides) - Update api_key_login doc to describe runtime overlay instead of env var mutation [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * fix: use LazyLock::lock() for INJECTED_VARS; use set_runtime_env() in bootstrap fallback - helpers.rs: fix env_or_override() to call INJECTED_VARS.lock() instead of .get() — INJECTED_VARS was changed upstream from OnceLock<HashMap> to LazyLock<Mutex<HashMap>>; calling .get() caused a compile error (E0599: no method named 'get' for LazyLock) - bootstrap.rs: when load_ironclaw_env() is called with an active Tokio runtime, use set_runtime_env("DATABASE_BACKEND", "libsql") instead of silently dropping the write. This ensures DATABASE_BACKEND is always set regardless of thread context (addresses ilblackdragon review item 1). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Gabe Hamilton <[email protected]> Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
co-authored by
Gabe Hamilton
Claude Sonnet 4.6
parent
8bbb43da52
commit
a9821ac20f
+9
-12
@@ -373,9 +373,10 @@ impl SessionManager {
|
||||
/// NEAR AI Cloud API key entry flow.
|
||||
///
|
||||
/// Prompts the user to enter a NEAR AI Cloud API key from
|
||||
/// cloud.near.ai. The key is set as `NEARAI_API_KEY` env var so
|
||||
/// `LlmConfig::resolve()` auto-selects ChatCompletions mode, and
|
||||
/// saved to `~/.ironclaw/.env` for persistence across restarts.
|
||||
/// cloud.near.ai. The key is stored in the thread-safe runtime
|
||||
/// env overlay (via `set_runtime_env`) so `LlmConfig::resolve()`
|
||||
/// auto-selects ChatCompletions mode, and persisted to
|
||||
/// `~/.ironclaw/.env` for survival across restarts.
|
||||
/// No session token is saved and no `/v1/users/me` validation is
|
||||
/// performed (different auth model).
|
||||
async fn api_key_login(&self) -> Result<(), LlmError> {
|
||||
@@ -403,15 +404,11 @@ impl SessionManager {
|
||||
});
|
||||
}
|
||||
|
||||
// Set env var so Config picks it up immediately
|
||||
// (LlmConfig::resolve() auto-selects ChatCompletions mode when
|
||||
// NEARAI_API_KEY is present).
|
||||
//
|
||||
// SAFETY: called during single-threaded interactive login flow.
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
std::env::set_var("NEARAI_API_KEY", &key);
|
||||
}
|
||||
// Make the key visible to Config resolution and `env_or_override()`
|
||||
// callers for the remainder of this process. Uses a thread-safe
|
||||
// overlay instead of `std::env::set_var`, which is UB in
|
||||
// multi-threaded programs (Rust 1.82+).
|
||||
crate::config::helpers::set_runtime_env("NEARAI_API_KEY", &key);
|
||||
|
||||
// Persist to ~/.ironclaw/.env so the key survives restarts
|
||||
// (bootstrap layer — available before DB is connected).
|
||||
|
||||
Reference in New Issue
Block a user