From c94fbb2b20c314bc0df40e14eaa5d053b306cb71 Mon Sep 17 00:00:00 2001 From: Rob Swindell <rob@synchro.net> Date: Wed, 19 Jan 2022 19:21:14 -0800 Subject: [PATCH] Add JS methods for vetting filenames (e.g. for upload by users) system.illegal_filename() - check if contains illegal chars/sequences system.safest_filename() - check if contains only safest chars system.allowed_filename() - check if meets criteria from SCFG->File Options system.check_filename() - check if legal and meets configured criteria and is not in file.can bbs.check_filename() - ditto, except will display badfile.msg as appropriate Now scripts can utilize the sysop-controlled filename criteria and security of the BBS. --- src/sbbs3/js_bbs.cpp | 34 +++++++++++ src/sbbs3/js_system.c | 130 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/src/sbbs3/js_bbs.cpp b/src/sbbs3/js_bbs.cpp index 59d5b8eb05..a28b9ed1fa 100644 --- a/src/sbbs3/js_bbs.cpp +++ b/src/sbbs3/js_bbs.cpp @@ -1387,6 +1387,34 @@ js_user_event(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } +static JSBool +js_checkfname(JSContext *cx, uintN argc, jsval *arglist) +{ + jsval *argv = JS_ARGV(cx, arglist); + sbbs_t* sbbs; + char* fname = NULL; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + if(argc < 1 || !JSVAL_IS_STRING(argv[0])) + return JS_TRUE; + + if((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL) + return JS_FALSE; + + JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL); + if(fname == NULL) + return JS_FALSE; + + rc=JS_SUSPENDREQUEST(cx); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->checkfname(fname))); + JS_RESUMEREQUEST(cx, rc); + free(fname); + + return JS_TRUE; +} + static JSBool js_chksyspass(JSContext *cx, uintN argc, jsval *arglist) { @@ -4632,6 +4660,12 @@ static jsSyncMethodSpec js_bbs_functions[] = { ,316 }, /* security */ + {"check_filename", js_checkfname, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") + ,JSDOCSTR("verify that the specified <i>filename</i> string is legal and allowed for upload " + "(based on system configuration), returns <i>true</i> if the filename is allowed.<br>" + "Note: Will display <tt>text/badfile.msg</tt> for matching filenames, if it exists.") + ,31902 + }, {"check_syspass", js_chksyspass, 0, JSTYPE_BOOLEAN, JSDOCSTR("[sys_pw]") ,JSDOCSTR("verify system password, prompting for the password if not passed as an argument") ,310 diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c index d25b40ee52..f353f35152 100644 --- a/src/sbbs3/js_system.c +++ b/src/sbbs3/js_system.c @@ -1924,6 +1924,117 @@ js_chkname(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } + +static JSBool +js_chkfname(JSContext *cx, uintN argc, jsval *arglist) +{ + JSObject *obj = JS_THIS_OBJECT(cx, arglist); + jsval *argv = JS_ARGV(cx, arglist); + char* fname = NULL; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + if(argc < 1 || !JSVAL_IS_STRING(argv[0])) + return JS_TRUE; + + js_system_private_t* sys; + if((sys = (js_system_private_t*)js_GetClassPrivate(cx,obj,&js_system_class))==NULL) + return JS_FALSE; + + JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL); + if(fname == NULL) + return JS_FALSE; + + rc=JS_SUSPENDREQUEST(cx); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(!illegal_filename(fname) + && allowed_filename(sys->cfg, fname) + && !trashcan(sys->cfg, fname, "file"))); + JS_RESUMEREQUEST(cx, rc); + free(fname); + + return JS_TRUE; +} + +static JSBool +js_safest_fname(JSContext *cx, uintN argc, jsval *arglist) +{ + JSObject *obj = JS_THIS_OBJECT(cx, arglist); + jsval *argv = JS_ARGV(cx, arglist); + char* fname = NULL; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + if(argc < 1 || !JSVAL_IS_STRING(argv[0])) + return JS_TRUE; + + JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL); + if(fname == NULL) + return JS_FALSE; + + rc=JS_SUSPENDREQUEST(cx); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(safest_filename(fname))); + JS_RESUMEREQUEST(cx, rc); + free(fname); + + return JS_TRUE; +} + +static JSBool +js_illegal_fname(JSContext *cx, uintN argc, jsval *arglist) +{ + JSObject *obj = JS_THIS_OBJECT(cx, arglist); + jsval *argv = JS_ARGV(cx, arglist); + char* fname = NULL; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + if(argc < 1 || !JSVAL_IS_STRING(argv[0])) + return JS_TRUE; + + JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL); + if(fname == NULL) + return JS_FALSE; + + rc=JS_SUSPENDREQUEST(cx); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(illegal_filename(fname))); + JS_RESUMEREQUEST(cx, rc); + free(fname); + + return JS_TRUE; +} + +static JSBool +js_allowed_fname(JSContext *cx, uintN argc, jsval *arglist) +{ + JSObject *obj = JS_THIS_OBJECT(cx, arglist); + jsval *argv = JS_ARGV(cx, arglist); + char* fname = NULL; + jsrefcount rc; + + JS_SET_RVAL(cx, arglist, JSVAL_FALSE); + + if(argc < 1 || !JSVAL_IS_STRING(argv[0])) + return JS_TRUE; + + js_system_private_t* sys; + if((sys = (js_system_private_t*)js_GetClassPrivate(cx,obj,&js_system_class))==NULL) + return JS_FALSE; + + JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL); + if(fname == NULL) + return JS_FALSE; + + rc=JS_SUSPENDREQUEST(cx); + JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(allowed_filename(sys->cfg, fname))); + JS_RESUMEREQUEST(cx, rc); + free(fname); + + return JS_TRUE; +} + #endif static JSBool @@ -2128,6 +2239,25 @@ static jsSyncMethodSpec js_system_functions[] = { "returns <i>true</i> if it is valid") ,315 }, + {"check_filename", js_chkfname, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") + ,JSDOCSTR("verify that the specified <i>filename</i> string is legal and allowed for upload by users " + "(based on system configuration), returns <i>true</i> if the filename is allowed") + ,31902 + }, + {"allowed_filename", js_allowed_fname, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") + ,JSDOCSTR("verify that the specified <i>filename</i> string is allowed for upload by users " + "(based on system configuration), returns <i>true</i> if the filename is allowed") + ,31902 + }, + {"safest_filename", js_safest_fname, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") + ,JSDOCSTR("verify that the specified <i>filename</i> string contains only the safest subset of characters") + ,31902 + }, + {"illegal_filename", js_illegal_fname, 1, JSTYPE_BOOLEAN, JSDOCSTR("filename") + ,JSDOCSTR("check if the specified <i>filename</i> string contains illegal characters or sequences, " + "returns <i>true</i> if it is an illegal filename") + ,31902 + }, #endif {"check_pid", js_chkpid, 1, JSTYPE_BOOLEAN, JSDOCSTR("process-ID") ,JSDOCSTR("checks that the provided process ID is a valid executing process on the system, " -- GitLab