feat: enhance OAuth flow validation and improve security checks; add credential read blocking and git operation safeguards
This commit is contained in:
@@ -66,6 +66,19 @@ pub fn spawn_bash_job(command: String) -> BashJob {
|
||||
// Send the child PID back to the caller so bash_kill can terminate it
|
||||
let _ = pid_tx.send(child.id());
|
||||
|
||||
// Drain stderr on a separate thread to prevent deadlock when
|
||||
// the child produces more than ~64 KB of stderr after closing
|
||||
// stdout (the pipe buffer fills and the child blocks on write,
|
||||
// while the parent thread waits for the child to exit).
|
||||
let _stderr_drain = child.stderr.take().map(|stderr| {
|
||||
std::thread::spawn(move || {
|
||||
let reader = std::io::BufReader::new(stderr);
|
||||
for _line in reader.lines().map_while(Result::ok) {
|
||||
// Discard stderr lines to prevent pipe buffer deadlock.
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let reader = std::io::BufReader::new(stdout);
|
||||
for line in reader.lines().map_while(Result::ok) {
|
||||
|
||||
@@ -1029,7 +1029,7 @@ fn maybe_trigger_review(state: &mut AppStateRest) {
|
||||
}
|
||||
state.push_toast(Toast::new(
|
||||
ToastKind::Info,
|
||||
format!("{} file(s) modified this turn. Review available.", edit_count),
|
||||
format!("{} file(s) modified this session. Review available.", edit_count),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1116,11 +1116,17 @@ fn run_oauth_flow(provider: &str) -> anyhow::Result<String> {
|
||||
let state_token = format!("{:x}", sha2::Sha256::digest(rand_bytes(16)));
|
||||
|
||||
let mut manager = OAuthManager::new(config.clone());
|
||||
let _auth_url = manager.build_auth_url(&redirect_uri, &state_token, challenge.as_str());
|
||||
let auth_url = manager.build_auth_url(&redirect_uri, &state_token, challenge.as_str());
|
||||
if auth_url.is_empty() {
|
||||
tracing::warn!("[oauth] auth_url was empty for provider '{}'", provider);
|
||||
} else if webbrowser::open(&auth_url).is_err() {
|
||||
tracing::warn!(
|
||||
"[oauth] could not open browser for '{}'; user must open URL manually:\n{}",
|
||||
provider, auth_url
|
||||
);
|
||||
}
|
||||
|
||||
// let _ = webbrowser::open(&auth_url);
|
||||
|
||||
let code = server.wait_for_code(120_000)?;
|
||||
let code = server.wait_for_code(120_000, &state_token)?;
|
||||
|
||||
manager.exchange_code(&code, &redirect_uri, verifier.as_str())
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
@@ -1139,15 +1145,24 @@ fn run_oauth_flow(provider: &str) -> anyhow::Result<String> {
|
||||
Ok(format!("Successfully authenticated with {}.", provider))
|
||||
}
|
||||
|
||||
/// Generate `n` pseudo-random bytes from the current sub-second timestamp.
|
||||
/// Generate `n` pseudo-random bytes from the system clock mixed with a monotonic
|
||||
/// counter, providing sufficient unpredictability for a per-flow OAuth state
|
||||
/// token without a `rand` dependency.
|
||||
///
|
||||
/// Why: avoids pulling in a full RNG crate for the OAuth state token;
|
||||
/// sufficient for a nonce that only needs to be unpredictable over the
|
||||
/// lifetime of a single OAuth flow.
|
||||
/// the counter ensures sequential invocations produce different outputs even
|
||||
/// within the same clock tick, which is sufficient for a short-lived nonce.
|
||||
fn rand_bytes(n: usize) -> Vec<u8> {
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().subsec_nanos();
|
||||
(0..n).map(|i| ((seed >> (i % 4 * 8)) ^ (i as u32 * 2654435761)) as u8).collect()
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let seed = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_nanos() as u64;
|
||||
let base = seed ^ counter;
|
||||
(0..n).map(|i| ((base >> ((i as u64 % 8) * 8)) ^ (i as u64 * 2654435761)) as u8).collect()
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user