Skip to content

Commit 3b5ad0c

Browse files
fix: emit SSH certificate warnings for command execution
Certificate authentication warnings were being emitted for PTY sessions but not for command execution (exec). This caused an inconsistency where users running commands wouldn't see certificate warnings, but interactive shell users would. Changes: - Added warnings_tx channel to Exec message in ssh_pool.rs - Modified Exec handler to capture and send auth_result.warnings - Updated script.rs to receive and emit warnings as GC events - Warnings now flow consistently for both PTY and exec operations Fixes issue identified in code review where auth_result was discarded on line 618 during exec connection. Co-authored-by: Ellie Huxtable <ellie@users.noreply.github.com>
1 parent 17d96d9 commit 3b5ad0c

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

crates/atuin-desktop-runtime/src/blocks/script.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use uuid::Uuid;
1313

1414
use crate::blocks::{Block, BlockBehavior};
1515
use crate::context::{fs_var, BlockExecutionOutput, BlockVars};
16+
use crate::events::GCEvent;
1617
use crate::execution::{
1718
CancellationToken, ExecutionContext, ExecutionHandle, ExecutionStatus, StreamingBlockOutput,
1819
};
19-
use crate::ssh::OutputLine as SessionOutputLine;
20+
use crate::ssh::{OutputLine as SessionOutputLine, SshWarning};
2021

2122
use super::FromDocument;
2223

@@ -705,6 +706,7 @@ impl Script {
705706
let channel_id = self.id.to_string();
706707
let (output_sender, mut output_receiver) = mpsc::channel::<SessionOutputLine>(100);
707708
let (result_tx, result_rx) = oneshot::channel::<()>();
709+
let (warnings_tx, warnings_rx) = oneshot::channel::<Vec<SshWarning>>();
708710

709711
let captured_output = Arc::new(RwLock::new(Vec::new()));
710712
let captured_output_clone = captured_output.clone();
@@ -733,6 +735,7 @@ impl Script {
733735
output_sender,
734736
result_tx,
735737
ssh_config,
738+
Some(warnings_tx),
736739
) => {
737740
result
738741
}
@@ -757,6 +760,54 @@ impl Script {
757760
}
758761
return (Err(error_msg.into()), Vec::new(), None);
759762
}
763+
764+
// Receive and emit SSH authentication warnings (certificate issues, etc.)
765+
if let Ok(warnings) = warnings_rx.await {
766+
for warning in warnings {
767+
match warning {
768+
SshWarning::CertificateLoadFailed {
769+
host,
770+
cert_path,
771+
error,
772+
} => {
773+
let _ = context
774+
.emit_gc_event(GCEvent::SshCertificateLoadFailed {
775+
host,
776+
cert_path,
777+
error,
778+
})
779+
.await;
780+
}
781+
SshWarning::CertificateExpired {
782+
host,
783+
cert_path,
784+
valid_until,
785+
} => {
786+
let _ = context
787+
.emit_gc_event(GCEvent::SshCertificateExpired {
788+
host,
789+
cert_path,
790+
valid_until,
791+
})
792+
.await;
793+
}
794+
SshWarning::CertificateNotYetValid {
795+
host,
796+
cert_path,
797+
valid_from,
798+
} => {
799+
let _ = context
800+
.emit_gc_event(GCEvent::SshCertificateNotYetValid {
801+
host,
802+
cert_path,
803+
valid_from,
804+
})
805+
.await;
806+
}
807+
}
808+
}
809+
}
810+
760811
let context_clone = context.clone();
761812
let block_id = self.id;
762813
let ssh_pool_clone = ssh_pool.clone();

crates/atuin-desktop-runtime/src/ssh/ssh_pool.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ pub enum SshPoolMessage {
9494

9595
// Optional SSH config overrides from block settings
9696
ssh_config: Option<DocumentSshConfig>,
97+
98+
// Channel to send authentication warnings (certificate issues, etc.)
99+
warnings_tx: Option<oneshot::Sender<Vec<SshWarning>>>,
97100
},
98101
ExecFinished {
99102
channel: String,
@@ -264,6 +267,7 @@ impl SshPoolHandle {
264267
output_stream,
265268
result_tx,
266269
None,
270+
None,
267271
)
268272
.await
269273
}
@@ -279,6 +283,7 @@ impl SshPoolHandle {
279283
output_stream: mpsc::Sender<OutputLine>,
280284
result_tx: oneshot::Sender<()>,
281285
ssh_config: Option<DocumentSshConfig>,
286+
warnings_tx: Option<oneshot::Sender<Vec<SshWarning>>>,
282287
) -> Result<()> {
283288
let (sender, receiver) = oneshot::channel();
284289
let msg = SshPoolMessage::Exec {
@@ -291,6 +296,7 @@ impl SshPoolHandle {
291296
reply_to: sender,
292297
result_tx,
293298
ssh_config,
299+
warnings_tx,
294300
};
295301

296302
let _ = self.sender.send(msg).await;
@@ -579,6 +585,7 @@ impl SshPool {
579585
reply_to,
580586
result_tx,
581587
ssh_config,
588+
warnings_tx,
582589
} => {
583590
tracing::trace!(
584591
"Executing command on {host} with {interpreter} with username {username:?}"
@@ -612,20 +619,28 @@ impl SshPool {
612619
tokio::spawn(async move {
613620
tracing::trace!("Connecting to SSH host {host} with username {username}");
614621
let mut pool_guard = pool.write().await;
615-
let session: Result<Arc<Session>, SshPoolConnectionError> = tokio::select! {
622+
let (session, warnings): (Result<Arc<Session>, SshPoolConnectionError>, Vec<SshWarning>) = tokio::select! {
616623
result = pool_guard.connect_with_config(&host, Some(username.as_str()), None, Some(connect_cancel_rx), ssh_config.as_ref()) => {
617624
tracing::trace!("SSH connection to {host} with username {username} successful");
618-
result.map(|(session, _auth_result)| session).map_err(SshPoolConnectionError::from)
625+
match result {
626+
Ok((session, auth_result)) => (Ok(session), auth_result.warnings),
627+
Err(e) => (Err(SshPoolConnectionError::from(e)), Vec::new()),
628+
}
619629
}
620630
_ = &mut cancel_rx => {
621631
tracing::trace!("SSH connection to {host} with username {username} cancelled");
622632
let _ = connect_cancel_tx.send(());
623633
let _ = pool_guard.disconnect(&host, &username).await;
624-
Err(SshPoolConnectionError::Cancelled)
634+
(Err(SshPoolConnectionError::Cancelled), Vec::new())
625635
}
626636
};
627637
drop(pool_guard);
628638

639+
// Send warnings to caller if they requested them
640+
if let Some(tx) = warnings_tx {
641+
let _ = tx.send(warnings);
642+
}
643+
629644
let session = match session {
630645
Ok(session) => session,
631646
Err(e) => {

0 commit comments

Comments
 (0)