diff --git a/code/controllers/configuration/config_entry.dm b/code/controllers/configuration/config_entry.dm
index 5358073dbea..b439743a799 100644
--- a/code/controllers/configuration/config_entry.dm
+++ b/code/controllers/configuration/config_entry.dm
@@ -51,7 +51,13 @@
return FALSE
return ..()
+/datum/config_entry/proc/VASProcCallGuard(str_val)
+ . = !(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
+ if(!.)
+ log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]")
+
/datum/config_entry/proc/ValidateAndSet(str_val)
+ VASProcCallGuard(str_val)
CRASH("Invalid config entry type!")
/datum/config_entry/proc/ValidateKeyedList(str_val, list_mode, splitter)
@@ -92,6 +98,8 @@
return var_name != "auto_trim" && ..()
/datum/config_entry/string/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
value = auto_trim ? trim(str_val) : str_val
return TRUE
@@ -103,6 +111,8 @@
var/min_val = -INFINITY
/datum/config_entry/number/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
var/temp = text2num(trim(str_val))
if(!isnull(temp))
value = Clamp(integer ? round(temp) : temp, min_val, max_val)
@@ -120,6 +130,8 @@
abstract_type = /datum/config_entry/flag
/datum/config_entry/flag/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
value = text2num(trim(str_val)) != 0
return TRUE
@@ -128,6 +140,8 @@
value = list()
/datum/config_entry/number_list/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
str_val = trim(str_val)
var/list/new_list = list()
var/list/values = splittext(str_val," ")
@@ -147,6 +161,8 @@
dupes_allowed = TRUE
/datum/config_entry/keyed_flag_list/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
return ValidateKeyedList(str_val, LIST_MODE_FLAG, " ")
/datum/config_entry/keyed_number_list
@@ -159,6 +175,8 @@
return var_name != "splitter" && ..()
/datum/config_entry/keyed_number_list/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
return ValidateKeyedList(str_val, LIST_MODE_NUM, splitter)
/datum/config_entry/keyed_string_list
@@ -171,6 +189,8 @@
return var_name != "splitter" && ..()
/datum/config_entry/keyed_string_list/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
return ValidateKeyedList(str_val, LIST_MODE_TEXT, splitter)
#undef LIST_MODE_NUM
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 7624d2473d0..8df012e5deb 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -118,6 +118,9 @@ GLOBAL_PROTECT(config_dir)
stat("[name]:", statclick)
/datum/controller/configuration/proc/Get(entry_type)
+ if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
+ log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
+ return
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
@@ -128,6 +131,9 @@ GLOBAL_PROTECT(config_dir)
return E.value
/datum/controller/configuration/proc/Set(entry_type, new_val)
+ if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
+ log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
+ return
var/datum/config_entry/E = entry_type
var/entry_is_abstract = initial(E.abstract_type) == entry_type
if(entry_is_abstract)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index ba42dbb9bae..cff80e6117d 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -96,10 +96,16 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
to_chat(usr, .)
SSblackbox.add_details("admin_verb","Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-GLOBAL_VAR_INIT(AdminProcCaller, null)
+GLOBAL_VAR(AdminProcCaller)
GLOBAL_PROTECT(AdminProcCaller)
GLOBAL_VAR_INIT(AdminProcCallCount, 0)
GLOBAL_PROTECT(AdminProcCallCount)
+GLOBAL_VAR(LastAdminCalledTargetRef)
+GLOBAL_PROTECT(LastAdminCalledTargetRef)
+GLOBAL_VAR(LastAdminCalledTarget)
+GLOBAL_PROTECT(LastAdminCalledTarget)
+GLOBAL_VAR(LastAdminCalledProc)
+GLOBAL_PROTECT(LastAdminCalledProc)
/proc/WrapAdminProcCall(target, procname, list/arguments)
var/current_caller = GLOB.AdminProcCaller
@@ -108,6 +114,9 @@ GLOBAL_PROTECT(AdminProcCallCount)
to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish.")
UNTIL(!GLOB.AdminProcCaller)
to_chat(usr, "Running your proc")
+ GLOB.LastAdminCalledProc = procname
+ if(target != GLOBAL_PROC)
+ GLOB.LastAdminCalledTargetRef = "\ref[target]"
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
++GLOB.AdminProcCallCount
. = world.WrapAdminProcCall(target, procname, arguments)