131 lines
4.4 KiB
Rust
131 lines
4.4 KiB
Rust
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use galaxyui::elements::Empty;
|
|
use galaxyui::platform::WindowStyle;
|
|
use galaxyui::{App, AppContext, Element, Entity, ModelHandle, TypedActionView, View, ViewContext};
|
|
|
|
use super::command_executor::testing::TestCommandExecutor;
|
|
use super::{BootstrapSessionType, Session, SessionId, SessionInfo, Sessions, SessionsEvent};
|
|
|
|
struct TestView {
|
|
events: Vec<SessionsEvent>,
|
|
}
|
|
|
|
impl Entity for TestView {
|
|
type Event = usize;
|
|
}
|
|
|
|
impl View for TestView {
|
|
fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
|
|
Empty::new().finish()
|
|
}
|
|
|
|
fn ui_name() -> &'static str {
|
|
"TestView"
|
|
}
|
|
}
|
|
|
|
impl TypedActionView for TestView {
|
|
type Action = ();
|
|
}
|
|
|
|
impl TestView {
|
|
fn new(model: ModelHandle<Sessions>, ctx: &mut ViewContext<Self>) -> Self {
|
|
ctx.subscribe_to_model(&model, |me, _, event, _| {
|
|
me.events.push(event.to_owned());
|
|
});
|
|
Self { events: Vec::new() }
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_set_env_var_emits_event() {
|
|
App::test((), |mut app| async move {
|
|
let model_handle = app.add_model(|_| Sessions::new_for_test());
|
|
let session_id: SessionId = 0.into();
|
|
let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |ctx| {
|
|
TestView::new(model_handle.clone(), ctx)
|
|
});
|
|
view_handle.read(&app, |view, _ctx| {
|
|
assert!(view.events.is_empty());
|
|
});
|
|
model_handle.update(&mut app, |sessions, ctx| {
|
|
let new_vars = HashMap::from_iter([("foo".to_string(), "bar".to_string())]);
|
|
sessions.set_env_vars_for_session(session_id, new_vars, ctx)
|
|
});
|
|
|
|
view_handle.read(&app, |view, _ctx| {
|
|
assert_eq!(view.events.len(), 1);
|
|
let expected_session_id = session_id;
|
|
let event = view.events.first().expect("checked length already");
|
|
if let SessionsEvent::EnvironmentVariablesUpdated { session_id } = event {
|
|
assert_eq!(*session_id, expected_session_id);
|
|
} else {
|
|
assert!(matches!(
|
|
event,
|
|
SessionsEvent::EnvironmentVariablesUpdated { .. }
|
|
));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_set_env_var_emits_no_event_when_no_change() {
|
|
App::test((), |mut app| async move {
|
|
let model_handle = app.add_model(|_| Sessions::new_for_test());
|
|
let session_id: SessionId = 0.into();
|
|
let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |ctx| {
|
|
TestView::new(model_handle.clone(), ctx)
|
|
});
|
|
view_handle.read(&app, |view, _ctx| {
|
|
assert!(view.events.is_empty());
|
|
});
|
|
model_handle.update(&mut app, |sessions, ctx| {
|
|
let new_vars = HashMap::from_iter([("foo".to_string(), "bar".to_string())]);
|
|
sessions.set_env_vars_for_session(session_id, new_vars, ctx)
|
|
});
|
|
|
|
view_handle.read(&app, |view, _ctx| {
|
|
assert_eq!(view.events.len(), 1);
|
|
});
|
|
|
|
model_handle.update(&mut app, |sessions, ctx| {
|
|
let new_vars = HashMap::from_iter([("foo".to_string(), "bar".to_string())]);
|
|
sessions.set_env_vars_for_session(session_id, new_vars, ctx)
|
|
});
|
|
|
|
view_handle.read(&app, |view, _ctx| {
|
|
assert_eq!(view.events.len(), 1);
|
|
});
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn test_malicious_histfile_path_does_not_execute_injected_commands() {
|
|
App::test((), |_app| async move {
|
|
// If escaping is missing, `touch /tmp/warp_injection_test` would execute
|
|
// as a side effect of reading history.
|
|
let marker = "/tmp/warp_injection_test";
|
|
// Clean up in case a previous broken run left the marker.
|
|
let _ = std::fs::remove_file(marker);
|
|
|
|
let malicious_histfile = format!("/tmp/x'; touch {marker}; echo '");
|
|
|
|
let session_info = SessionInfo::new_for_test()
|
|
.with_session_type(BootstrapSessionType::WarpifiedRemote)
|
|
.with_histfile(Some(malicious_histfile));
|
|
let session = Session::new(session_info, Arc::new(TestCommandExecutor::default()));
|
|
|
|
// read_history for a WarpifiedRemote session calls read_history_from_file,
|
|
// which builds `cat '{escaped_path}'` and executes it via TestCommandExecutor
|
|
let _ = session.read_history(false).await;
|
|
|
|
assert!(
|
|
!std::path::Path::new(marker).exists(),
|
|
"Injected command executed — escaping regression!"
|
|
);
|
|
});
|
|
}
|