fix: respect that the port of the expected_origin can be zero (#1295) (#1337)

This commit is contained in:
Myriad-Dreamin 2025-02-20 01:04:33 +08:00 committed by GitHub
parent 5a05d12fdd
commit 6d2de24a05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -13,6 +13,7 @@ use hyper_tungstenite::{tungstenite::Message, HyperWebsocket, HyperWebsocketStre
use hyper_util::rt::TokioIo;
use hyper_util::server::graceful::GracefulShutdown;
use lsp_types::notification::Notification;
use lsp_types::Url;
use parking_lot::Mutex;
use reflexo_typst::debug_loc::SourceSpanOffset;
use reflexo_typst::{error::prelude::*, Error};
@ -451,6 +452,13 @@ pub async fn make_http_server(
use hyper::body::{Bytes, Incoming};
type Server = hyper_util::server::conn::auto::Builder<hyper_util::rt::TokioExecutor>;
let listener = tokio::net::TcpListener::bind(&static_file_addr)
.await
.unwrap();
let addr = listener.local_addr().unwrap();
log::info!("preview server listening on http://{addr}");
let expected_port = addr.port();
let frontend_html = hyper::body::Bytes::from(frontend_html);
let expected_origin = format!("http://{static_file_addr}");
let make_service = move || {
@ -474,9 +482,11 @@ pub async fn make_http_server(
// think that's okay from a security point of view, because
// I think malicious websites can't trick browsers into sending
// `vscode-webview://...` as `Origin`.
if req.headers().get("Origin").is_some_and(|h| {
*h == expected_origin || h.as_bytes().starts_with(b"vscode-webview://")
}) {
if req
.headers()
.get("Origin")
.is_some_and(|h| is_valid_origin(h, &expected_origin, expected_port))
{
let (response, websocket) = hyper_tungstenite::upgrade(&mut req, None)
.map_err(|e| {
log::error!("Error in websocket upgrade: {e}");
@ -537,12 +547,6 @@ pub async fn make_http_server(
})
};
let listener = tokio::net::TcpListener::bind(&static_file_addr)
.await
.unwrap();
let addr = listener.local_addr().unwrap();
log::info!("preview server listening on http://{addr}");
let (shutdown_tx, rx) = tokio::sync::oneshot::channel();
let (final_tx, final_rx) = tokio::sync::oneshot::channel();
@ -604,6 +608,24 @@ pub async fn make_http_server(
}
}
fn is_valid_origin(h: &HeaderValue, expected_origin: &str, expected_port: u16) -> bool {
*h == expected_origin || h.as_bytes().starts_with(b"vscode-webview://") || {
// Matches the origin of the local listening port.
let matched = std::str::from_utf8(h.as_bytes()).ok().and_then(|h| {
let url = Url::parse(h).ok()?;
(url.port() == Some(expected_port)
&& matches!(url.scheme(), "http" | "https" | "ws" | "wss")
&& matches!(
// todo: allocate memory here.
url.host().as_ref().map(ToString::to_string).as_deref(),
Some("localhost" | "127.0.0.1")
))
.then_some(())
});
matched.is_some()
}
}
/// Entry point of the preview tool.
pub async fn preview_main(args: PreviewCliArgs) -> Result<()> {
log::info!("Arguments: {args:#?}");