mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
fix(console): prevent DOM-based XSS in console views and escape JS strings
- Validate and coerce console query parameters (view_only, scale, resize_session, clip_viewport) to strict booleans via to_bool in console/views.py - Sanitize view parameter to strictly 'lite' or 'full' - Apply escapejs filter to all JS string interpolations in console-vnc-full.html, console-vnc-lite.html, console-spice-lite.html, and console-xterm.html - Replace unsafe '| safe' filter in console-spice-lite.html with escapejs - Add unit tests for XSS query param mitigation and escapejs character encoding
This commit is contained in:
parent
3d4accf1b4
commit
f19c30840e
6 changed files with 102 additions and 35 deletions
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
// By default, use the host and port of server that served this file
|
||||
// host = spice_query_var('host', window.location.hostname);
|
||||
host = '{{ ws_host| safe }}';
|
||||
host = '{{ ws_host|escapejs }}';
|
||||
|
||||
// Note that using the web server port only makes sense
|
||||
// if your web server has a reverse proxy to relay the WebSocket
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
}
|
||||
}
|
||||
//port = spice_query_var('port', default_port);
|
||||
port = '{{ ws_port| safe }}';
|
||||
port = '{{ ws_port|escapejs }}';
|
||||
if (window.location.protocol == 'https:') {
|
||||
scheme = "wss://";
|
||||
}
|
||||
|
|
@ -93,17 +93,16 @@
|
|||
}
|
||||
|
||||
if (password === undefined) {
|
||||
{% if perms.instances.passwordless_console %}
|
||||
password = '{{ console_passwd }}';
|
||||
{% if perms.instances.passwordless_console and console_passwd %}
|
||||
password = '{{ console_passwd|escapejs }}';
|
||||
{% else %}
|
||||
password = prompt('{% trans "Password" %}');
|
||||
{% endif %}
|
||||
//password = '{{ console_passwd | safe }}';
|
||||
}
|
||||
if (password === 'None') password = '';
|
||||
|
||||
//var path = spice_query_var('path', 'websockify');
|
||||
var path = spice_query_var('path', '{{ ws_path }}');
|
||||
var path = spice_query_var('path', '{{ ws_path|escapejs }}');
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
console.log('{% trans "must specify host and port in URL" %}');
|
||||
|
|
|
|||
|
|
@ -176,24 +176,24 @@
|
|||
bindKeyAction('ctrlaltf' + (i + 1), () => sendCtrlAltFN(i));
|
||||
}
|
||||
|
||||
let fullPath = '{{ ws_path }}';
|
||||
const token = '{{ token }}';
|
||||
let fullPath = '{{ ws_path|escapejs }}';
|
||||
const token = '{{ token|escapejs }}';
|
||||
if (token) {
|
||||
fullPath += (fullPath.includes('?') ? '&' : '?') + 'token=' + encodeURIComponent(token);
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
autoconnect: true,
|
||||
resize: '{{ scale }}' === 'true' ? 'scale' : 'off',
|
||||
view_only: {{ view_only }},
|
||||
view_clip: {{ clip_viewport }},
|
||||
resize: {{ scale|yesno:"'scale','off'" }},
|
||||
view_only: {{ view_only|yesno:"true,false" }},
|
||||
view_clip: {{ clip_viewport|yesno:"true,false" }},
|
||||
{% if perms.instances.passwordless_console and console_passwd %}
|
||||
password: '{{ console_passwd }}',
|
||||
password: '{{ console_passwd|escapejs }}',
|
||||
{% endif %}
|
||||
};
|
||||
|
||||
const mandatory = {
|
||||
host: '{{ ws_host }}',
|
||||
host: '{{ ws_host|escapejs }}',
|
||||
port: {{ ws_port }},
|
||||
path: fullPath,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -260,17 +260,17 @@
|
|||
|
||||
// Read parameters specified in the URL query string
|
||||
// By default, use the host and port of server that served this file
|
||||
const host = readQueryVariable('host', '{{ ws_host }}');
|
||||
let port = readQueryVariable('port', '{{ ws_port }}');
|
||||
const host = readQueryVariable('host', '{{ ws_host|escapejs }}');
|
||||
let port = readQueryVariable('port', '{{ ws_port|escapejs }}');
|
||||
|
||||
{% if perms.instances.passwordless_console %}
|
||||
const password = '{{ console_passwd }}';
|
||||
{% if perms.instances.passwordless_console and console_passwd %}
|
||||
const password = '{{ console_passwd|escapejs }}';
|
||||
{% else %}
|
||||
const password = readQueryVariable('password');
|
||||
{% endif %}
|
||||
|
||||
//const path = readQueryVariable('path', 'websockify');
|
||||
const path = readQueryVariable('path', '{{ ws_path }}');
|
||||
const path = readQueryVariable('path', '{{ ws_path|escapejs }}');
|
||||
|
||||
// | | | | | |
|
||||
// | | | Connect | | |
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
} else {
|
||||
url += '/';
|
||||
}
|
||||
const token = readQueryVariable('token', '{{ token }}');
|
||||
const token = readQueryVariable('token', '{{ token|escapejs }}');
|
||||
if (token) {
|
||||
url += (url.includes('?') ? '&' : '?') + 'token=' + encodeURIComponent(token);
|
||||
}
|
||||
|
|
@ -310,10 +310,10 @@
|
|||
rfb.addEventListener("desktopname", updateDesktopName);
|
||||
|
||||
// Set parameters that can be changed on an active connection
|
||||
rfb.scaleViewport = {{ scale }};
|
||||
rfb.viewOnly = {{ view_only }};
|
||||
rfb.resizeSession = {{ resize_session }};
|
||||
rfb.clipViewport = {{ clip_viewport }};
|
||||
rfb.scaleViewport = {{ scale|yesno:"true,false" }};
|
||||
rfb.viewOnly = {{ view_only|yesno:"true,false" }};
|
||||
rfb.resizeSession = {{ resize_session|yesno:"true,false" }};
|
||||
rfb.clipViewport = {{ clip_viewport|yesno:"true,false" }};
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
<script crossorigin="anonymous">
|
||||
Terminal.applyAddon(fit);
|
||||
|
||||
let host = readQueryVariable('host', '{{ socketio_host }}');
|
||||
let port = readQueryVariable('port', '{{ socketio_port }}');
|
||||
let path = readQueryVariable('path', '{{ socketio_path }}');
|
||||
let host = readQueryVariable('host', '{{ socketio_host|escapejs }}');
|
||||
let port = readQueryVariable('port', '{{ socketio_port|escapejs }}');
|
||||
let path = readQueryVariable('path', '{{ socketio_path|escapejs }}');
|
||||
|
||||
|
||||
// Build the websocket URL used to connect
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue