Browse Source

Fix previewer security issue

pull/12757/head
Daniil Pavliuchyk 3 years ago
parent
commit
030d7c2f3b
  1. 44
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
  2. 6
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts
  3. 5
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html

44
src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs

@ -23,6 +23,9 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
private SimpleWebSocket _pendingSocket;
private bool _disposed;
private object _lock = new object();
private Uri _listenUri;
private Guid _secretCookie;
private bool _isFirstMessage = true;
private AutoResetEvent _wakeup = new AutoResetEvent(false);
private FrameMessage _lastFrameMessage = null;
private FrameMessage _lastSentFrameMessage = null;
@ -42,6 +45,7 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
if (listenUri.Scheme != "http")
throw new ArgumentException("URI scheme is not HTTP.", nameof(listenUri));
_listenUri = listenUri;
var resourcePrefix = "Avalonia.DesignerSupport.Remote.HtmlTransport.webapp.build.";
_resources = typeof(HtmlWebSocketTransport).Assembly.GetManifestResourceNames()
.Where(r => r.StartsWith(resourcePrefix, StringComparison.OrdinalIgnoreCase)
@ -57,10 +61,21 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
{
var ms = new MemoryStream();
s.CopyTo(ms);
return ms.ToArray();
var currentIndexBytes = ms.ToArray();
if (r == resourcePrefix + "index.html.gz" && ms.CanWrite)
{
_secretCookie = Guid.NewGuid();
var resultIndexString = Encoding.Default.GetString(currentIndexBytes)
.Replace("PREVIEWER_SECURITY_COOKIE", _secretCookie.ToString());
var resultIndexBytes = Encoding.UTF8.GetBytes(resultIndexString);
ms.Write(resultIndexBytes, 0, resultIndexBytes.Length);
return resultIndexBytes;
}
return currentIndexBytes;
}
});
_signalTransport = signalTransport;
var address = IPAddress.Parse(listenUri.Host);
@ -98,12 +113,19 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
}
else
{
var socket = await req.AcceptWebSocket();
SocketReceiveWorker(socket);
lock (_lock)
if (req.Headers.TryGetValue("Origin", out var origin) && origin == _listenUri.OriginalString)
{
_pendingSocket?.Dispose();
_pendingSocket = socket;
var socket = await req.AcceptWebSocket();
SocketReceiveWorker(socket);
lock (_lock)
{
_pendingSocket?.Dispose();
_pendingSocket = socket;
}
}
else
{
throw new Exception("Origin doesen't match Url");
}
}
}
@ -114,13 +136,19 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
{
try
{
if(_isFirstMessage && (await socket.ReceiveMessage().ConfigureAwait(false)).AsString() != _secretCookie.ToString())
{
socket.Dispose();
return;
}
_isFirstMessage = false;
while (true)
{
var msg = await socket.ReceiveMessage().ConfigureAwait(false);
if(msg != null && msg.IsText)
{
var message = ParseMessage(msg.AsString());
if (message != null)
if (message != null)
_onMessage?.Invoke(this, message);
}
}

6
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts

@ -41,7 +41,7 @@ export class PreviewerServerConnection {
const onMessage = this.onMessage;
conn.onmessage = msg => onMessage(msg);
conn.onopen = open => this.onOpen(open);
const onClose = () => this.setFrame(null);
conn.onclose = () => onClose();
conn.onerror = (err: Event) => {
@ -50,6 +50,10 @@ export class PreviewerServerConnection {
}
}
private onOpen(open: Event) {
this.conn.send(window["avaloniaPreviewerSecurityCookie"]);
}
private onMessage = (msg: MessageEvent) => {
if (typeof msg.data == 'string' || msg.data instanceof String) {
const parts = msg.data.split(':');

5
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html

@ -10,6 +10,9 @@
<center>Loading...</center>
</div>
<noscript>JavaScript is required</noscript>
<script>
window["avaloniaPreviewerSecurityCookie"] = "PREVIEWER_SECURITY_COOKIE";
</script>
<script src="index.tsx"></script>
</body>
</html>
</html>

Loading…
Cancel
Save