99
1010import asyncio
1111import json
12+ import secrets
1213import socket
1314import webbrowser
1415from http .server import HTTPServer , SimpleHTTPRequestHandler
@@ -50,10 +51,26 @@ class PlaygroundHandler(SimpleHTTPRequestHandler):
5051 so the project key is never exposed to the browser."""
5152
5253 client : MossClient | None = None
54+ _token : str = ""
55+ _server_host : str = ""
5356
5457 def __init__ (self , * args , ** kwargs ):
5558 super ().__init__ (* args , directory = str (HERE / "playground" ), ** kwargs )
5659
60+ def _check_api_request (self ) -> bool :
61+ if self .headers .get ("X-Moss-Token" ) != self ._token :
62+ self ._send_json (403 , {"error" : "Forbidden: invalid or missing token" })
63+ return False
64+ host = self .headers .get ("Host" , "" )
65+ if host and host != self ._server_host and host != self ._server_host .replace ("127.0.0.1" , "localhost" ):
66+ self ._send_json (403 , {"error" : "Forbidden: invalid Host header" })
67+ return False
68+ origin = self .headers .get ("Origin" , "" )
69+ if origin and origin != f"http://{ self ._server_host } " :
70+ self ._send_json (403 , {"error" : "Forbidden: invalid Origin" })
71+ return False
72+ return True
73+
5774 def _send_json (self , status : int , data : dict ) -> None :
5875 body = json .dumps (data ).encode ("utf-8" )
5976 self .send_response (status )
@@ -81,8 +98,12 @@ def do_GET(self) -> None:
8198 self .send_response (204 )
8299 self .end_headers ()
83100 elif path == "/api/indexes" :
101+ if not self ._check_api_request ():
102+ return
84103 self ._handle_list_indexes ()
85104 elif path == "/api/index" :
105+ if not self ._check_api_request ():
106+ return
86107 names = params .get ("name" , [])
87108 self ._handle_get_index (names [0 ] if names else None )
88109 else :
@@ -93,6 +114,10 @@ def _serve_index(self) -> None:
93114 self ._send_json (500 , {"error" : "Playground HTML not found" })
94115 return
95116 html = PLAYGROUND_HTML .read_text (encoding = "utf-8" )
117+ html = html .replace (
118+ "</title>" ,
119+ f'</title>\n <meta name="moss-token" content="{ self ._token } " />' ,
120+ )
96121 self ._send_html (html )
97122
98123 def _handle_list_indexes (self ) -> None :
@@ -129,6 +154,8 @@ def log_message(self, format, *args):
129154 console .print (f" [dim]{ args [0 ]} [/dim]" )
130155
131156 def do_POST (self ) -> None :
157+ if not self ._check_api_request ():
158+ return
132159 parsed = urlparse (self .path )
133160 path = parsed .path
134161 length = int (self .headers .get ("Content-Length" , 0 ))
@@ -238,6 +265,8 @@ def playground_command(
238265 server_addr = ("127.0.0.1" , final_port )
239266
240267 PlaygroundHandler .client = client
268+ PlaygroundHandler ._token = secrets .token_urlsafe (32 )
269+ PlaygroundHandler ._server_host = f"127.0.0.1:{ final_port } "
241270
242271 server = HTTPServer (server_addr , PlaygroundHandler )
243272 url = f"http://127.0.0.1:{ final_port } "
0 commit comments