From 9013f866a8231f39f066fc9bd9638611da2c3962 Mon Sep 17 00:00:00 2001
From: "Rob Swindell (on ChromeOS)" <rob@synchro.net>
Date: Sat, 30 Mar 2024 15:26:20 -0700
Subject: [PATCH] Save and restore the js.exec_path, exec_dir, and exec_file
 properties

When invoking a nested JS script, these properties of the "js" object would
be overwritten and not restored, as discovered/reported by Nightfox when his
trivial game script would indirectly execute yesnobar.js, his subsequent use
of js.exec_dir would point to the wrong location (the "exec" directory, parent
of yesnobar.js, and not the direcctory where his game script was located).
The exec_path and exec_file properties had the same problem as demonstrated
by a simple test.js placed in (and executed from) a directory other than the
"exec" dir:
	function f() {
        print("js.exec_path = " + js.exec_path);
        print("js.exec_dir = " + js.exec_dir);
        print("Js.exec_file = " + js.exec_file);
	}
	f();
	console.yesno("test");
	f();

This would only trigger the problem when executed from the BBS and whebn the
YesNoQuestion text.dat string invokes the "yesnobar" module via EXEC @-code and
yesnobar.js exists (in exec or mods dir), superceding yesnobar.bin which does
not trigger this issue (not a JavaScript mod).
---
 src/sbbs3/exec.cpp | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/src/sbbs3/exec.cpp b/src/sbbs3/exec.cpp
index 454d7031a4..1f28e7802d 100644
--- a/src/sbbs3/exec.cpp
+++ b/src/sbbs3/exec.cpp
@@ -541,6 +541,10 @@ int sbbs_t::js_execfile(const char *cmd, const char* startup_dir, JSObject* scop
 	JSObject*	js_script=NULL;
 	jsval		old_js_argv = JSVAL_VOID;
 	jsval		old_js_argc = JSVAL_VOID;
+	jsval		old_js_exec_path = JSVAL_VOID;
+	jsval		old_js_exec_file = JSVAL_VOID;
+	jsval		old_js_exec_dir = JSVAL_VOID;
+	jsval		val;
 	jsval		rval;
 	int32		result=0;
 	struct js_event_list	*events;
@@ -663,6 +667,17 @@ int sbbs_t::js_execfile(const char *cmd, const char* startup_dir, JSObject* scop
 		js_callback.counter=0;	// Reset loop counter
 	}
 	JS_SetOperationCallback(js_cx, js_OperationCallback);
+	if(JS_GetProperty(js_cx, js_glob, "js", &val) && JSVAL_IS_OBJECT(val)) {
+		JSObject* js = JSVAL_TO_OBJECT(val);
+
+		if(JS_GetProperty(js_cx, js, "exec_path", &old_js_exec_path))
+			JS_AddValueRoot(js_cx, &old_js_exec_path);
+		if(JS_GetProperty(js_cx, js, "exec_file", &old_js_exec_file))
+			JS_AddValueRoot(js_cx, &old_js_exec_file);
+		if(JS_GetProperty(js_cx, js, "exec_dir", &old_js_exec_dir))
+			JS_AddValueRoot(js_cx, &old_js_exec_dir);
+	}
+
 	js_PrepareToExecute(js_cx, js_glob, path, startup_dir, js_scope);
 	events = js_callback.events;
 	js_callback.events = NULL;
@@ -702,6 +717,18 @@ int sbbs_t::js_execfile(const char *cmd, const char* startup_dir, JSObject* scop
 		JS_RemoveValueRoot(js_cx, &old_js_argv);
 		JS_RemoveValueRoot(js_cx, &old_js_argc);
 	}
+	if(JS_GetProperty(js_cx, js_glob, "js", &val) && JSVAL_IS_OBJECT(val)) {
+		JSObject* js = JSVAL_TO_OBJECT(val);
+		JS_DefineProperty(js_cx, js, "exec_path", old_js_exec_path
+			,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
+		JS_RemoveValueRoot(js_cx, &old_js_exec_path);
+		JS_DefineProperty(js_cx, js, "exec_file", old_js_exec_file
+			,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
+		JS_RemoveValueRoot(js_cx, &old_js_exec_file);
+		JS_DefineProperty(js_cx, js, "exec_dir", old_js_exec_dir
+			,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
+		JS_RemoveValueRoot(js_cx, &old_js_exec_dir);
+	}
 
 	JS_GC(js_cx);
 
-- 
GitLab