Rewrite script processing a bit

- Instead of using eval() we now use a proper VM Context
- As a result, any scripting errors will now give a more useful filename and line number.
- However, some things may break, if they are dependant on variables we are not allowing in the context.
This commit is contained in:
zefie
2022-09-25 18:02:25 -04:00
parent 90f4896e72
commit d8da9e14c7
3 changed files with 68 additions and 8 deletions

View File

@@ -30,7 +30,7 @@ if (socket.ssid) {
delete ssid_sessions[socket.ssid].data_store.wtvsec_login; delete ssid_sessions[socket.ssid].data_store.wtvsec_login;
} }
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config); ssid_sessions[socket.ssid].data_store.wtvsec_login = ssid_sessions[socket.ssid].createWTVSecSession();
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge(); ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
if (request_headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]); if (request_headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
} else { } else {

View File

@@ -66,6 +66,10 @@ class WTVClientSessionData {
this.mailstore = new WTVMail(this.minisrv_config, this) this.mailstore = new WTVMail(this.minisrv_config, this)
} }
createWTVSecSession() {
return new WTVSec(this.minisrv_config)
}
getAccountTotalUnreadMessages() { getAccountTotalUnreadMessages() {
if (!this.isRegistered()) return false; // unregistered if (!this.isRegistered()) return false; // unregistered
if (this.user_id > 0) return false; // not primary user or pre-login if (this.user_id > 0) return false; // not primary user or pre-login

View File

@@ -17,6 +17,7 @@ const WTVClientSessionData = require('./WTVClientSessionData.js');
const WTVMime = require("./WTVMime.js"); const WTVMime = require("./WTVMime.js");
const { WTVShared, clientShowAlert } = require("./WTVShared.js"); const { WTVShared, clientShowAlert } = require("./WTVShared.js");
const WTVFlashrom = require("./WTVFlashrom.js"); const WTVFlashrom = require("./WTVFlashrom.js");
const vm = require('vm');
process process
.on('SIGTERM', shutdown('SIGTERM')) .on('SIGTERM', shutdown('SIGTERM'))
@@ -107,6 +108,36 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
var service_vault_found = false; var service_vault_found = false;
var service_path = unescape(service_vault_file_path); var service_path = unescape(service_vault_file_path);
var usingSharedROMCache = false; var usingSharedROMCache = false;
// Here we define the ServiceVault Script Context Object
// The ServiceVault scripts will only be allowed to access the following variables.
// Furthermore, only modifications to "headers", "data", "ssid_sessions" and "socket_sessions" will be saved.
// Example: an attempt to change "minisrv_config" from a ServiceVault script would be discarded
var contextObj = {
console: console,
Buffer: Buffer,
require: require,
wtvmime: wtvmime,
wtvshared: wtvshared,
clientShowAlert: clientShowAlert,
strftime: strftime,
CryptoJS: CryptoJS,
fs: fs,
__dirname: __dirname,
minisrv_config: minisrv_config,
getServiceString: getServiceString,
sendToClient: sendToClient,
socket: socket,
request_headers: request_headers,
service_name: service_name,
service_vaults: service_vaults,
ssid_sessions: ssid_sessions,
socket_sessions: socket_sessions,
headers: headers,
data: data,
request_is_async: request_is_async,
String: String,
Object: Object
}
try { try {
service_vaults.forEach(function (service_vault_dir) { service_vaults.forEach(function (service_vault_dir) {
if (service_vault_found) return; if (service_vault_found) return;
@@ -161,7 +192,6 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
request_is_async = true; request_is_async = true;
request_headers.service_file_path = service_vault_file_path; request_headers.service_file_path = service_vault_file_path;
request_headers.raw_file = true; request_headers.raw_file = true;
// process flashroms // process flashroms
if (wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "rom" || wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "brom") { if (wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "rom" || wtvshared.getFileExt(service_vault_file_path).toLowerCase() == "brom") {
var bf0app_update = false; var bf0app_update = false;
@@ -262,8 +292,22 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
// expose var service_dir for script path to the root of the wtv-service // expose var service_dir for script path to the root of the wtv-service
var service_dir = service_vault_dir + path.sep + service_name; var service_dir = service_vault_dir + path.sep + service_name;
socket_sessions[socket.id].starttime = Math.floor(new Date().getTime() / 1000); socket_sessions[socket.id].starttime = Math.floor(new Date().getTime() / 1000);
var jscript_eval = fs.readFileSync(service_vault_file_path + ".js").toString(); var script_data = fs.readFileSync(service_vault_file_path + ".js").toString();
eval(jscript_eval); var eval_ctx = new vm.Script(script_data, {
"filename": service_vault_file_path + ".js"
})
vm.createContext(contextObj);
eval_ctx.runInContext(contextObj, {
"breakOnSigint": true
});
// Here we read back certain data from the ServiceVault Script Context Object
headers = contextObj.headers;
data = contextObj.data;
request_is_async = contextObj.request_is_async;
ssid_sessions = contextObj.ssid_sessions;
socket_sessions = contextObj.socket_sessions;
if (request_is_async && !minisrv_config.config.debug_flags.quiet) console.log(" * Script requested Asynchronous mode"); if (request_is_async && !minisrv_config.config.debug_flags.quiet) console.log(" * Script requested Asynchronous mode");
} }
else if (fs.existsSync(service_vault_file_path + ".html")) { else if (fs.existsSync(service_vault_file_path + ".html")) {
@@ -293,10 +337,22 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
service_vault_found = true; service_vault_found = true;
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found catchall at " + catchall_file + " to handle request (JS Interpreter Mode) [Socket " + socket.id + "]"); if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found catchall at " + catchall_file + " to handle request (JS Interpreter Mode) [Socket " + socket.id + "]");
request_headers.service_file_path = catchall_file; request_headers.service_file_path = catchall_file;
var jscript_eval = fs.readFileSync(catchall_file).toString(); var script_data = fs.readFileSync(catchall_file).toString();
// don't pass these vars to the script var eval_ctx = new vm.Script(script_data, {
var service_check_dir, minisrv_catchall_file_name = null; "filename": catchall_file
eval(jscript_eval); })
vm.createContext(contextObj);
eval_ctx.runInContext(contextObj, {
"breakOnSigint": true
});
// Here we read back certain data from the ServiceVault Script Context Object
headers = contextObj.headers;
data = contextObj.data;
request_is_async = contextObj.request_is_async;
ssid_sessions = contextObj.ssid_sessions;
socket_sessions = contextObj.socket_sessions;
if (request_is_async && !minisrv_config.config.debug_flags.quiet) console.log(" * Script requested Asynchronous mode"); if (request_is_async && !minisrv_config.config.debug_flags.quiet) console.log(" * Script requested Asynchronous mode");
} else { } else {
service_check_dir.pop(); service_check_dir.pop();