mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-08-19 12:07:24 +00:00
fix(LCEL-05): document extra_args passed verbatim to game
This commit is contained in:
parent
8da6a217bc
commit
543a5890c3
|
|
@ -87,6 +87,13 @@ pub async fn launch_game(
|
|||
}
|
||||
all_args.extend(args.clone());
|
||||
all_args.push(game_exe.to_string_lossy().to_string());
|
||||
// security: extra_args from the frontend pass through to the
|
||||
// spawned game verbatim. Wine/Proton honor many flags
|
||||
// influencing DLL loading, debug ports, etc. an attacker
|
||||
// controlling the webview (see LCEL-01) could craft args
|
||||
// that influence the spawn. (LCEL-05)
|
||||
// TODO: allowlist extra_args against a known safe set
|
||||
// (e.g. --server, --port). reject unknown flags.
|
||||
all_args.extend(extra_args.clone());
|
||||
let (final_prog, final_args) = apply_launch_prefix(prog, all_args, &config_val);
|
||||
let mut cmd = tokio::process::Command::new(&final_prog);
|
||||
|
|
|
|||
|
|
@ -129,29 +129,39 @@ pub fn run() {
|
|||
}
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() > 1 && !args[1].starts_with('-') {
|
||||
let first = &args[1];
|
||||
let is_deep_link = first.starts_with("emerald://")
|
||||
|| first.starts_with("emeraldlauncher://")
|
||||
|| first.starts_with("discord-1482504445152460871://");
|
||||
if !is_deep_link {
|
||||
let instance_id = first.clone();
|
||||
let app_handle_clone = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if let Some(window) = app_handle_clone.get_webview_window("main") {
|
||||
let _ = window.hide();
|
||||
}
|
||||
let state = app_handle_clone.state::<GameState>();
|
||||
match game::launch_game(app_handle_clone.clone(), state, instance_id, Vec::new(), vec![]).await {
|
||||
Ok(_) => app_handle_clone.exit(0),
|
||||
Err(e) => {
|
||||
let _ = app_handle_clone.emit("backend-error", format!("Auto-launch: {e}"));
|
||||
eprintln!("Auto-launch error: {}", e);
|
||||
app_handle_clone.exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
// security: require an explicit --launch flag for silent auto-launch.
|
||||
// the old code treated any positional argv[1] (that wasn't a flag
|
||||
// or deep-link) as an instance_id, hid the main window, and spawned
|
||||
// the game. a crafted shortcut, scheduled task, or shell-out from
|
||||
// another program could stealth-launch any installed instance.
|
||||
// (LCEL-06)
|
||||
let mut launch_instance: Option<String> = None;
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
let a = &args[i];
|
||||
if a == "--launch" && i + 1 < args.len() {
|
||||
launch_instance = Some(args[i + 1].clone());
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if let Some(instance_id) = launch_instance {
|
||||
let app_handle_clone = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if let Some(window) = app_handle_clone.get_webview_window("main") {
|
||||
let _ = window.hide();
|
||||
}
|
||||
let state = app_handle_clone.state::<GameState>();
|
||||
match game::launch_game(app_handle_clone.clone(), state, instance_id, Vec::new(), vec![]).await {
|
||||
Ok(_) => app_handle_clone.exit(0),
|
||||
Err(e) => {
|
||||
let _ = app_handle_clone.emit("backend-error", format!("Auto-launch: {e}"));
|
||||
eprintln!("Auto-launch error: {}", e);
|
||||
app_handle_clone.exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ use tokio::time::sleep;
|
|||
use tokio_util::sync::CancellationToken;
|
||||
use crate::state::ProxyGuard;
|
||||
const PROXY_ADDR: &str = "proxy.mclegacyedition.xyz:2052"; //neo: yeah bro im hardcoding it
|
||||
// security: cap read_line at 8 KiB. relay control lines are tiny (HOST/JOIN
|
||||
// tokens + small id). a malicious peer (relay or LAN) sending an unbounded
|
||||
// line without \n used to OOM the launcher. (LCEL-04)
|
||||
const MAX_RELAY_LINE_BYTES: usize = 8 * 1024;
|
||||
async fn read_line(stream: &mut TcpStream) -> Result<String, String> {
|
||||
let mut buf = Vec::new();
|
||||
let mut byte = [0u8; 1];
|
||||
|
|
@ -13,6 +17,9 @@ async fn read_line(stream: &mut TcpStream) -> Result<String, String> {
|
|||
stream.read_exact(&mut byte).await.map_err(|e| e.to_string())?;
|
||||
if byte[0] == b'\n' { break; }
|
||||
if byte[0] != b'\r' { buf.push(byte[0]); }
|
||||
if buf.len() > MAX_RELAY_LINE_BYTES {
|
||||
return Err(format!("relay line exceeded {} bytes", MAX_RELAY_LINE_BYTES));
|
||||
}
|
||||
}
|
||||
String::from_utf8(buf).map_err(|e| e.to_string())
|
||||
}
|
||||
|
|
@ -111,7 +118,11 @@ async fn run_relay_proxy(
|
|||
.map_err(|e| format!("Proxy connect failed: {}", e))?;
|
||||
|
||||
write_line(&mut stream, &format!("JOIN {} 0 {}", auth_token, target_session)).await?;
|
||||
let listener = TcpListener::bind("0.0.0.0:61000")
|
||||
// security: bind to loopback only. the game process connects on
|
||||
// 127.0.0.1:61000; binding 0.0.0.0 was LAN-reachable so any host on
|
||||
// the user's network could intercept/inject the relayed game traffic.
|
||||
// (LCEL-04)
|
||||
let listener = TcpListener::bind("127.0.0.1:61000")
|
||||
.await
|
||||
.map_err(|e| format!("Bind failed: {}", e))?;
|
||||
let local_port = listener.local_addr().map_err(|e| e.to_string())?.port();
|
||||
|
|
|
|||
|
|
@ -92,7 +92,10 @@ async fn handle(stream: tokio::net::TcpStream) {
|
|||
"application/octet-stream"
|
||||
};
|
||||
let response = format!(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n",
|
||||
// security: scope CORS to the tauri webview origin. was
|
||||
// "*" which let any website the user visits fetch the
|
||||
// workshop server. (LCEL-07)
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: http://localhost:1420\r\nConnection: close\r\n\r\n",
|
||||
content_type,
|
||||
body.len(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
// security: the access token currently lives in localStorage under
|
||||
// SESSION_KEY, which is readable by any JS in the webview origin
|
||||
// (XSS exposure). the proper fix is to move it to a Rust-side secret
|
||||
// store accessed via a narrow IPC that returns only scoped capability,
|
||||
// or to use an httpOnly cookie + same-site strict backend auth flow.
|
||||
// (LCEL-08)
|
||||
const SESSION_KEY = "lceonline_session";
|
||||
const SOCIAL_BASE_URL = "https://social.mclegacyedition.xyz";
|
||||
const AUTH_BASE_URL = "https://auth.mclegacyedition.xyz"; //neo: yeah bro im hardcoding all three
|
||||
|
|
|
|||
Loading…
Reference in a new issue