KASM-8264 fixed implicit globals, update vars, change loops, fix vulns

This commit is contained in:
Chris Hunt 2026-04-15 10:35:14 +01:00
parent 9bae28c7d4
commit d7d5cd8f12
No known key found for this signature in database
3 changed files with 65 additions and 66 deletions

View file

@ -1,33 +1,32 @@
// Imports // Imports
var Docker = require('dockerode'); const Docker = require('dockerode');
var socketIO = require('socket.io'); const socketIO = require('socket.io');
var pty = require("node-pty"); const pty = require("node-pty");
var fsw = require('fs').promises; const fsw = require('fs').promises;
var fs = require('fs'); const fs = require('fs');
var os = require('os'); const os = require('os');
var yaml = require('js-yaml'); const yaml = require('js-yaml');
var _ = require('lodash'); const _ = require('lodash');
var si = require('systeminformation'); const si = require('systeminformation');
var express = require('express'); const express = require('express');
var app = require('express')(); const app = require('express')();
var privateKey = fs.readFileSync('/opt/kasm/certs/kasm_wizard.key', 'utf8'); const privateKey = fs.readFileSync('/opt/kasm/certs/kasm_wizard.key', 'utf8');
var certificate = fs.readFileSync('/opt/kasm/certs/kasm_wizard.crt', 'utf8'); const certificate = fs.readFileSync('/opt/kasm/certs/kasm_wizard.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate}; const credentials = {key: privateKey, cert: certificate};
var https = require('https').Server(credentials, app); const https = require('https').Server(credentials, app);
var baserouter = express.Router(); const baserouter = express.Router();
var docker = new Docker({socketPath: '/var/run/docker.sock'}); const docker = new Docker({socketPath: '/var/run/docker.sock'});
var arch = os.arch().replace('x64', 'amd64'); const arch = os.arch().replace('x64', 'amd64');
var baseUrl = process.env.SUBFOLDER || '/'; const baseUrl = process.env.SUBFOLDER || '/';
var version = process.env.VERSION || 'stable'; const port = process.env.KASM_PORT || '443';
var port = process.env.KASM_PORT || '443';
const { spawn } = require('node:child_process'); const { spawn } = require('node:child_process');
var EULA; let EULA;
var images; let images;
var currentVersion; let currentVersion;
var sourceLocal = false; let sourceLocal = false;
var gpuInfo; let gpuInfo;
var installSettings = {}; let installSettings = {};
var upgradeSettings = {}; let upgradeSettings = {};
// Find the best matching compatibility entry for the current version. // Find the best matching compatibility entry for the current version.
// Exact version match takes precedence over wildcard (e.g. "1.18.x"). // Exact version match takes precedence over wildcard (e.g. "1.18.x").
@ -186,17 +185,17 @@ async function setGpu(imagesI) {
let card = gpu.slice(-1); let card = gpu.slice(-1);
let render = (Number(card) + 128).toString(); let render = (Number(card) + 128).toString();
// Handle NVIDIA Gpus // Handle NVIDIA Gpus
var baseRun; let baseRun;
if (gpuName.indexOf('NVIDIA') !== -1) { if (gpuName.indexOf('NVIDIA') !== -1) {
baseRun = JSON.parse('{"runtime":"nvidia","environment":{"NVIDIA_DRIVER_CAPABILITIES":"all","KASM_EGL_CARD":"/dev/dri/card' + card + '","KASM_RENDERD":"/dev/dri/renderD' + render + '"},"device_requests":[{"driver": "","count": -1,"device_ids": null,"capabilities":[["gpu"]],"options":{}}]}'); baseRun = JSON.parse('{"runtime":"nvidia","environment":{"NVIDIA_DRIVER_CAPABILITIES":"all","KASM_EGL_CARD":"/dev/dri/card' + card + '","KASM_RENDERD":"/dev/dri/renderD' + render + '"},"device_requests":[{"driver": "","count": -1,"device_ids": null,"capabilities":[["gpu"]],"options":{}}]}');
} else { } else {
baseRun = JSON.parse('{"environment":{"DRINODE":"/dev/dri/renderD' + render + '", "HW3D": true},"devices":["/dev/dri/card' + card + ':/dev/dri/card' + card + ':rwm","/dev/dri/renderD' + render + ':/dev/dri/renderD' + render + ':rwm"]}'); baseRun = JSON.parse('{"environment":{"DRINODE":"/dev/dri/renderD' + render + '", "HW3D": true},"devices":["/dev/dri/card' + card + ':/dev/dri/card' + card + ':rwm","/dev/dri/renderD' + render + ':/dev/dri/renderD' + render + ':rwm"]}');
} }
let baseExec = JSON.parse('{"first_launch":{"user":"root","cmd": "bash -c \'chown -R kasm-user:kasm-user /dev/dri/*\'"}}'); let baseExec = JSON.parse('{"first_launch":{"user":"root","cmd": "bash -c \'chown -R kasm-user:kasm-user /dev/dri/*\'"}}');
for (var i=0; i<imagesI.images.length; i++) { for (let i=0; i<imagesI.images.length; i++) {
console.log(imagesI.images[i]['run_config']); console.log(imagesI.images[i]['run_config']);
finalRun = _.merge(imagesI.images[i]['run_config'], baseRun) let finalRun = _.merge(imagesI.images[i]['run_config'], baseRun)
finalExec = _.merge(imagesI.images[i]['exec_config'], baseExec) let finalExec = _.merge(imagesI.images[i]['exec_config'], baseExec)
imagesI.images[i]['run_config'] = finalRun; imagesI.images[i]['run_config'] = finalRun;
imagesI.images[i]['exec_config'] = finalExec; imagesI.images[i]['exec_config'] = finalExec;
} }
@ -238,14 +237,14 @@ app.use(baseUrl, baserouter);
https.listen(3000); https.listen(3000);
//// socketIO comms //// //// socketIO comms ////
io = socketIO(https, {path: baseUrl + 'socket.io'}); const io = socketIO(https, {path: baseUrl + 'socket.io'});
io.on('connection', async function (socket) { io.on('connection', async function (socket) {
// Run bash install with our custom flags // Run bash install with our custom flags
async function install(data) { async function install(data) {
// Determine install settings // Determine install settings
installSettings = data[0]; installSettings = data[0];
var imagesI = data[1]; let imagesI = data[1];
installFlags = ['/kasm_release/install.sh', '-W', '-B' ,'-H', '-e', '-L', port, '-P', installSettings.adminPass, '-U', installSettings.userPass]; let installFlags = ['/kasm_release/install.sh', '-W', '-B' ,'-H', '-e', '-L', port, '-P', installSettings.adminPass, '-U', installSettings.userPass];
if (imagesI && typeof imagesI === 'object' && Array.isArray(imagesI.images) && imagesI.images.length < 10) { if (imagesI && typeof imagesI === 'object' && Array.isArray(imagesI.images) && imagesI.images.length < 10) {
installFlags.push('-b'); installFlags.push('-b');
} }
@ -281,7 +280,7 @@ io.on('connection', async function (socket) {
// Run bash upgrade with our custom flags // Run bash upgrade with our custom flags
async function upgrade(data) { async function upgrade(data) {
upgradeFlags = ['/kasm_release/upgrade.sh', '-L', port]; let upgradeFlags = ['/kasm_release/upgrade.sh', '-L', port];
// Patch service image tags for rolling builds // Patch service image tags for rolling builds
await appendRollingToServiceImages(); await appendRollingToServiceImages();

View file

@ -9,7 +9,7 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"dockerode": "^3.3.2", "dockerode": "^4.0.10",
"express": "^4.18.1", "express": "^4.18.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View file

@ -1,21 +1,21 @@
// Variables // Variables
var EULA; let EULA;
var images; let images;
var gpus; let gpus;
var term; let term;
var installImages = []; let installImages = [];
var installSettings = {}; const installSettings = {};
var upgradeSettings = {}; const upgradeSettings = {};
var currentVersion = ''; let currentVersion = '';
var isUpgrade = false; let isUpgrade = false;
var selected = false; let selected = false;
// Socket.io connection // Socket.io connection
var host = window.location.hostname; const host = window.location.hostname;
var port = window.location.port; const port = window.location.port;
var protocol = window.location.protocol; const protocol = window.location.protocol;
var path = window.location.pathname; const path = window.location.pathname;
var socket = io(protocol + '//' + host + ':' + port, { path: path + 'socket.io'}); const socket = io(protocol + '//' + host + ':' + port, { path: path + 'socket.io'});
//// Page Functions //// //// Page Functions ////
@ -33,7 +33,7 @@ async function install() {
if (installImages.length == 0) { if (installImages.length == 0) {
socket.emit('install', [installSettings, false]); socket.emit('install', [installSettings, false]);
} else { } else {
for await (let image of installImages) { for (let image of installImages) {
let srcImage = images.images.find(x => x.friendly_name === image); let srcImage = images.images.find(x => x.friendly_name === image);
if (!srcImage) { if (!srcImage) {
alert('Error: image "' + image + '" not found. Please refresh and try again.'); alert('Error: image "' + image + '" not found. Please refresh and try again.');
@ -71,7 +71,7 @@ function showTerminal() {
$('#terminal').empty(); $('#terminal').empty();
$('#terminal').css('display', 'block'); $('#terminal').css('display', 'block');
term = new Terminal(); term = new Terminal();
fitaddon = new FitAddon.FitAddon(); let fitaddon = new FitAddon.FitAddon();
term.loadAddon(fitaddon); term.loadAddon(fitaddon);
term.open($('#terminal')[0]); term.open($('#terminal')[0]);
fitaddon.fit(); fitaddon.fit();
@ -112,14 +112,14 @@ async function renderDash(data) {
// Store GPU info // Store GPU info
$('body').data('gpuInfo', info.gpuInfo); $('body').data('gpuInfo', info.gpuInfo);
// Upgrade button if needed // Upgrade button if needed
var upgrade; let upgrade;
if (info.currentVersion !== info.localVersion) { if (info.currentVersion !== info.localVersion) {
upgrade = $('<button>', {class: 'btn btn-primary', onclick: 'renderUpgrade()'}).text('Upgrade to ' + info.currentVersion) upgrade = $('<button>', {class: 'btn btn-primary', onclick: 'renderUpgrade()'}).text('Upgrade to ' + info.currentVersion)
} else { } else {
upgrade = info.currentVersion; upgrade = info.currentVersion;
} }
// Kasm docker containers // Kasm docker containers
containersTable = $('<tbody>'); let containersTable = $('<tbody>');
containersTable.append( containersTable.append(
$('<tr>').append([ $('<tr>').append([
$('<th>').text('Web URL'), $('<th>').text('Web URL'),
@ -134,7 +134,7 @@ async function renderDash(data) {
$('<td>').append(upgrade) $('<td>').append(upgrade)
]) ])
); );
for await (let container of info.containers) { for (let container of info.containers) {
containersTable.append( containersTable.append(
$('<tr>').append([ $('<tr>').append([
$('<th>').text(container.Names[0]), $('<th>').text(container.Names[0]),
@ -142,7 +142,7 @@ async function renderDash(data) {
]) ])
); );
} }
dockerCard = $('<div>', {id: 'dockerinfo', class: 'terminal-card'}).append([ let dockerCard = $('<div>', {id: 'dockerinfo', class: 'terminal-card'}).append([
$('<header>').text('Kasm Docker containers'), $('<header>').text('Kasm Docker containers'),
$('<table>').append(containersTable) $('<table>').append(containersTable)
]); ]);
@ -150,7 +150,7 @@ async function renderDash(data) {
let usedmem = (info.mem.active/info.mem.total)*100; let usedmem = (info.mem.active/info.mem.total)*100;
let totalmem = parseFloat(info.mem.total/1000000000).toFixed(2); let totalmem = parseFloat(info.mem.total/1000000000).toFixed(2);
let diskbuffer = parseFloat(info.mem.buffcache/1000000000).toFixed(2); let diskbuffer = parseFloat(info.mem.buffcache/1000000000).toFixed(2);
sysinfoTable = $('<tbody>').append([ let sysinfoTable = $('<tbody>').append([
$('<tr>').append([ $('<tr>').append([
$('<th>').text('CPU'), $('<th>').text('CPU'),
$('<td>').text(info.cpu.vendor + ' ' + info.cpu.brand) $('<td>').text(info.cpu.vendor + ' ' + info.cpu.brand)
@ -190,7 +190,7 @@ async function renderDash(data) {
) )
]) ])
]); ]);
systemCard = $('<div>', {id: 'systeminfo', class: 'terminal-card'}).append([ let systemCard = $('<div>', {id: 'systeminfo', class: 'terminal-card'}).append([
$('<header>').text('System Information'), $('<header>').text('System Information'),
$('<table>').append(sysinfoTable) $('<table>').append(sysinfoTable)
]); ]);
@ -231,7 +231,7 @@ async function pickSettings() {
$('<input>', {name: 'userPass', id: 'userPass', type: 'password', required: true, placeholder: 'required'}) $('<input>', {name: 'userPass', id: 'userPass', type: 'password', required: true, placeholder: 'required'})
]); ]);
let gpuOptions = [$('<option>', {value: 'disabled'}).text('Disabled')]; let gpuOptions = [$('<option>', {value: 'disabled'}).text('Disabled')];
for await (let card of Object.keys(gpus || {})) { for (let card of Object.keys(gpus || {})) {
gpuOptions.push($('<option>', {value: card + '|' + gpus[card]}).text(card + ' - ' + gpus[card])); gpuOptions.push($('<option>', {value: card + '|' + gpus[card]}).text(card + ' - ' + gpus[card]));
} }
let forceGpu = $('<div>', {class: 'form-group'}).append([ let forceGpu = $('<div>', {class: 'form-group'}).append([
@ -298,8 +298,8 @@ function renderUpgrade() {
// Render image selection // Render image selection
async function pickImages(upgrade) { async function pickImages(upgrade) {
var installText; let installText;
var installFunction; let installFunction;
if (upgrade) { if (upgrade) {
installText = 'Upgrade'; installText = 'Upgrade';
installFunction = 'upgrade()'; installFunction = 'upgrade()';
@ -311,7 +311,7 @@ async function pickImages(upgrade) {
titleChange('Image Selection'); titleChange('Image Selection');
let imagesDiv = $('<div>', {class: 'cardcontainer', id: 'images'}); let imagesDiv = $('<div>', {class: 'cardcontainer', id: 'images'});
$('#container').append(imagesDiv); $('#container').append(imagesDiv);
for await (let image of images.images) { for (let image of images.images) {
let imageName = $('<p>').text(image.friendly_name); let imageName = $('<p>').text(image.friendly_name);
let imageDiv = $('<div>', { let imageDiv = $('<div>', {
class: 'card', class: 'card',
@ -382,8 +382,8 @@ function selectAll() {
// Show finished page // Show finished page
function done(data) { function done(data) {
var port = (typeof data === 'object') ? data.port : data; const port = (typeof data === 'object') ? data.port : data;
var rollingTag = (typeof data === 'object') ? data.rollingTag : null; const rollingTag = (typeof data === 'object') ? data.rollingTag : null;
showContainer(); showContainer();
titleChange('Complete'); titleChange('Complete');
let titleBar = $('<div>'); let titleBar = $('<div>');