Skip to content

Commit

Permalink
VV expansion: refreshing list, viewable+EDITABLE special lists, filte…
Browse files Browse the repository at this point in the history
…rs beta (and mass clean up codes) (#11507)

* vv filters and list refresh

* vv expansions

* Fixes some possible bug cases

* Removes outdated comment

* Cleans up code, fixes an error

* Update vv_ghost.dm (more documentation)

* adds comments to fix the weird error

* I have no idea why this throws that error

* wrong line

* trying to fix weird lint error

* some fix

* removes weird indent

* cleaning up

* Clean up codes, preventing href exploit

* Make it more readible

* v2 cleaning up

* another clean up

* and more clean up

* final

* removes untyped var access

* vv review addressed: more safety

* more protection
  • Loading branch information
EvilDragonfiend authored Dec 24, 2024
1 parent c1b6f97 commit 72350f6
Show file tree
Hide file tree
Showing 10 changed files with 486 additions and 140 deletions.
1 change: 1 addition & 0 deletions beestation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,7 @@
#include "code\modules\admin\view_variables\topic_basic.dm"
#include "code\modules\admin\view_variables\topic_list.dm"
#include "code\modules\admin\view_variables\view_variables.dm"
#include "code\modules\admin\view_variables\vv_ghost.dm"
#include "code\modules\antagonists\eldritch.dm"
#include "code\modules\antagonists\toddsie.dm"
#include "code\modules\antagonists\_common\antag_datum.dm"
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ GLOBAL_VAR_INIT(magic_appearance_detecting_image, new /image) // appearances are

// The filters list has the same ref type id as a filter, but isnt one and also isnt a list, so we have to check if the thing has Cut() instead
GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
#define isfilter(thing) (!hascall(thing, "Cut") && TYPEID(thing) == GLOB.refid_filter)
#define isfilter(thing) (!islist(thing) && hascall(thing, "Cut") && TYPEID(thing) == GLOB.refid_filter)

GLOBAL_DATUM_INIT(regex_rgb_text, /regex, regex(@"^#?(([0-9a-fA-F]{8})|([0-9a-fA-F]{6})|([0-9a-fA-F]{3}))$"))
#define iscolortext(thing) (istext(thing) && GLOB.regex_rgb_text.Find(thing))
Expand Down
48 changes: 46 additions & 2 deletions code/__DEFINES/vv.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
#define VV_MSG_EDITED "<br><font size='1' color='red'><b>Var Edited</b></font>"
#define VV_MSG_DELETED "<br><font size='1' color='red'><b>Deleted</b></font>"

#define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50
#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150
#define VV_BIG_SIZED_LIST_THRESHOLD 50

//#define IS_VALID_ASSOC_KEY(V) (istext(V) || ispath(V) || isdatum(V) || islist(V))
#define IS_VALID_ASSOC_KEY(V) (!isnum_safe(V)) //hhmmm..
Expand All @@ -45,6 +44,9 @@
#define VV_HREF_TARGETREF(targetref, href_key, text) "<a href='[VV_HREF_TARGETREF_INTERNAL(targetref, href_key)]'>[text]</a>"
#define VV_HREF_TARGET_1V(target, href_key, text, varname) "<a href='[VV_HREF_TARGET_INTERNAL(target, href_key)];[VV_HK_VARNAME]=[varname]'>[text]</a>" //for stuff like basic varedits, one variable
#define VV_HREF_TARGETREF_1V(targetref, href_key, text, varname) "<a href='[VV_HREF_TARGETREF_INTERNAL(targetref, href_key)];[VV_HK_VARNAME]=[varname]'>[text]</a>"
//! Non-standard helper for special list vv. this doesn't use VV_HK_TARGET and REF because special list doesn't work in a sane sense.
#define VV_HREF_SPECIAL(dmlist_origin_ref, href_action, text, list_index, dmlist_varname) "<a href='?_src_=vars;[HrefToken()];[href_action]=TRUE;dmlist_origin_ref=[dmlist_origin_ref];dmlist_varname=[dmlist_varname];[VV_HK_VARNAME]=[list_index]'>[text]</a>"
#define VV_HREF_SPECIAL_MENU(dmlist_origin_ref, href_action, dmlist_varname) "?_src_=vars;[HrefToken()];[href_action]=TRUE;[VV_HK_DO_LIST_EDIT]=TRUE;dmlist_origin_ref=[dmlist_origin_ref];dmlist_varname=[dmlist_varname]"

#define GET_VV_TARGET locate(href_list[VV_HK_TARGET])
#define GET_VV_VAR_TARGET href_list[VV_HK_VARNAME]
Expand All @@ -71,6 +73,9 @@
#define VV_HK_LIST_SHUFFLE "listshuffle"
#define VV_HK_LIST_SET_LENGTH "listlen"

// I exist alone here just for special list edit. God, why.
#define VV_HK_DO_LIST_EDIT "do_vv_list_edit"

// vv_do_basic() keys
#define VV_HK_BASIC_EDIT "datumedit"
#define VV_HK_BASIC_CHANGE "datumchange"
Expand Down Expand Up @@ -182,3 +187,42 @@

/// ALWAYS render a reduced list, useful for fuckoff big datums that need to be condensed for the sake of client load
#define VV_ALWAYS_CONTRACT_LIST (1<<0)
#define VV_READ_ONLY (1<<1)


#define VV_LIST_PROTECTED (1) /// Can not vv the list. Doing vv this list is not safe.
#define VV_LIST_READ_ONLY (2) /// Can vv the list, but can not edit.
#define VV_LIST_EDITABLE (3) /// Can vv the list, and edit.

// Becomes read only at live, editable at debug, dynamically
#ifdef DEBUG
#define VV_LIST_READ_ONLY___DEBUG_EDITABLE (3)
#else
#define VV_LIST_READ_ONLY___DEBUG_EDITABLE (2)
#endif

/// A list of all the special byond lists that need to be handled different by vv.
/// manually adding var name is recommanded.
GLOBAL_LIST_INIT(vv_special_lists, list(
// /datum
"vars" = VV_LIST_READ_ONLY,
// /atom
"overlays" = VV_LIST_EDITABLE,
"underlays" = VV_LIST_EDITABLE,
"vis_contents" = VV_LIST_EDITABLE,
"vis_locs" = VV_LIST_READ_ONLY___DEBUG_EDITABLE,
"contents" = VV_LIST_EDITABLE,
"locs" = VV_LIST_READ_ONLY___DEBUG_EDITABLE,
"verbs" = VV_LIST_READ_ONLY___DEBUG_EDITABLE, // verb is not safe to edit in live server
"filters" = VV_LIST_PROTECTED, // This is not good to change in vv, yet.
// /client
"bounds" = VV_LIST_PROTECTED, // DM document says it's read-only. Better not to edit this.
"images" = VV_LIST_EDITABLE,
"screen" = VV_LIST_EDITABLE,
))
// NOTE: this is highly attached to how /datum/vv_ghost works.


#ifndef DEBUG
GLOBAL_PROTECT(vv_special_lists) // changing this in live server is a bad idea
#endif
16 changes: 0 additions & 16 deletions code/_globalvars/lists/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,3 @@
GLOBAL_LIST_INIT_TYPED(smite_list, /datum/smite, init_smites())

GLOBAL_VAR_INIT(admin_notice, "") // Admin notice that all clients see when joining the server

// A list of all the special byond lists that need to be handled different by vv
GLOBAL_LIST_INIT(vv_special_lists, init_special_list_names())

/proc/init_special_list_names()
var/list/output = list()
var/obj/sacrifice = new
for(var/varname in sacrifice.vars)
var/value = sacrifice.vars[varname]
if(!islist(value))
if(!isdatum(value) && hascall(value, "Cut"))
output += varname
continue
if(isnull(locate(REF(value))))
output += varname
return output
111 changes: 87 additions & 24 deletions code/modules/admin/view_variables/debug_variables.dm
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )

// defines of hints for how a proc should build strings
#define STYLE_READ_ONLY (1)
#define STYLE_NORMAL (2)
#define STYLE_LIST (3)
#define STYLE_SPECIAL (4)
#define STYLE_EMPTY (5)

/// Get displayed variable in VV variable list
/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value.
// variables to store values
var/index
var/list/owner_list
var/datum/vv_ghost/vv_spectre

// ------------------------------------------------------------
// checks if a thing is /list, or /vv_ghost to deliver a special list, and then reassign name/value
if(owner)
if(islist(owner))
var/index = name
var/list/owner_list = owner
if(istype(owner, /datum/vv_ghost))
vv_spectre = owner
if(islist(owner) || vv_spectre)
index = name
owner_list = vv_spectre?.dmlist_holder || owner
if (value)
name = owner_list[name] //name is really the index until this line
else
value = owner_list[name]
. = "<li style='backgroundColor:white'>([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_REMOVE, "-", index)]) "
else

// ------------------------------------------------------------
// Builds hyperlink strings with edit options
var/special_list_secure_level = (istext(name) && (isdatum(owner) || vv_spectre) ) ? GLOB.vv_special_lists[name] : null
var/is_read_only = CHECK_BITFIELD(display_flags, VV_READ_ONLY) || (special_list_secure_level && (special_list_secure_level <= VV_LIST_READ_ONLY))
var/hyperlink_style =\
(is_read_only && level) ? STYLE_EMPTY \
: is_read_only ? STYLE_READ_ONLY \
: vv_spectre ? STYLE_SPECIAL \
: owner_list ? STYLE_LIST \
: owner ? STYLE_NORMAL \
: STYLE_EMPTY

switch(hyperlink_style)
if(STYLE_READ_ONLY)
. = "<li style='backgroundColor:white'>(Disabled) "
if(STYLE_NORMAL)
. = "<li style='backgroundColor:white'>([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) "
else
. = "<li>"
if(STYLE_LIST)
. = "<li style='backgroundColor:white'>([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner_list, VV_HK_LIST_REMOVE, "-", index)]) "
if(STYLE_SPECIAL)
. = "<li style='backgroundColor:white'>([VV_HREF_SPECIAL(vv_spectre.dmlist_origin_ref, VV_HK_LIST_EDIT, "E", index, vv_spectre.dmlist_varname)]) ([VV_HREF_SPECIAL(vv_spectre.dmlist_origin_ref, VV_HK_LIST_CHANGE, "C", index, vv_spectre.dmlist_varname)]) ([VV_HREF_SPECIAL(vv_spectre.dmlist_origin_ref, VV_HK_LIST_REMOVE, "-", index, vv_spectre.dmlist_varname)]) "
if(STYLE_EMPTY)
. = "<li>"

// ------------------------------------------------------------
var/name_part = VV_HTML_ENCODE(name)
if(level > 0 || islist(owner)) //handling keys in assoc lists
if(istype(name,/datum))
Expand All @@ -30,7 +67,7 @@
return "[.][item]</li>"

// This is split into a seperate proc mostly to make errors that happen not break things too much
/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags)
/proc/_debug_variable_value(name, datum/value, level, datum/owner, sanitize, display_flags)
. = "<font color='red'>DISPLAY_ERROR:</font> ([value] [REF(value)])" // Make sure this line can never runtime

if(isnull(value))
Expand Down Expand Up @@ -60,9 +97,12 @@
var/image/image = value
return "<a href='?_src_=vars;[HrefToken()];Vars=[REF(value)]'>[image.type] (<span class='value'>[get_appearance_vv_summary_name(image)]</span>) [REF(value)]</a>"

if(isfilter(value))
var/datum/filter_value = value
return "/filter (<span class='value'>[filter_value.type] [REF(filter_value)]</span>)"
// fun fact: there are two types of /filters. `/filters(/filters(), /filters(), ...)`
// isfilter() doesn't know if it's a parent filter(that has [/filters]s inside of itself), or a child filter
var/isfilter = isfilter(value)
var/is_child_filter = isfilter && !isdatum(owner) && !isappearance(owner) // 'child_filter' means each /filters in /atom.filters
if(is_child_filter)
return "/filters\[child\] (<span class='value'>[value.type]</span>)"

if(isfile(value))
return "<span class='value'>'[value]'</span>"
Expand All @@ -71,17 +111,37 @@
var/datum/datum_value = value
return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags)

if(islist(value) || (name in GLOB.vv_special_lists)) // Some special lists arent detectable as a list through istype
var/special_list_secure_level = (istext(name) && isdatum(owner)) ? GLOB.vv_special_lists[name] : null
var/islist = islist(value) || special_list_secure_level
if(islist)
var/list/list_value = value
var/list/items = list()

// This is becuse some lists either dont count as lists or a locate on their ref will return null
var/link_vars = "Vars=[REF(value)]"
if(name in GLOB.vv_special_lists)
link_vars = "Vars=[REF(owner)];special_varname=[name]"

if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && list_value.len > 0 && list_value.len <= (IS_NORMAL_LIST(list_value) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD))
for (var/i in 1 to list_value.len)
var/list_type = \
isfilter ? "/filters\[parent\]" \
: special_list_secure_level ? "/special_list" \
: /list

// Hyperlink to open a /list window.
var/a_open = null
var/a_close = null

// some '/list' instance is dangerous to open.
var/can_open_list_window = !( (special_list_secure_level == VV_LIST_PROTECTED) || isappearance(owner) )
if(can_open_list_window)
var/href_reference_string = \
special_list_secure_level \
? "dmlist_origin_ref=[REF(owner)];dmlist_varname=[name]" \
: "Vars=[REF(value)]"
a_open = "<a href='?_src_=vars;[HrefToken()];[href_reference_string]'>"
a_close = "</a>"

var/should_fold_list_items = (display_flags & VV_ALWAYS_CONTRACT_LIST) || length(list_value) > VV_BIG_SIZED_LIST_THRESHOLD
if(can_open_list_window && should_fold_list_items)
return "[a_open][list_type] ([length(list_value)])[a_close]"
else
var/flag = (special_list_secure_level && (special_list_secure_level <= VV_LIST_READ_ONLY)) ? VV_READ_ONLY : null
var/list/items = list()
for (var/i in 1 to length(list_value))
var/key = list_value[i]
var/val
if (IS_NORMAL_LIST(list_value) && !isnum(key))
Expand All @@ -90,11 +150,9 @@
val = key
key = i

items += debug_variable(key, val, level + 1, sanitize = sanitize)
items += debug_variable(key, val, level + 1, sanitize = sanitize, display_flags = flag)

return "<a href='?_src_=vars;[HrefToken()];[link_vars]'>/list ([list_value.len])</a><ul>[items.Join()]</ul>"
else
return "<a href='?_src_=vars;[HrefToken()];[link_vars]'>/list ([list_value.len])</a>"
return "[a_open][list_type] ([length(list_value)])[a_close]<ul>[items.Join()]</ul>"

if(name in GLOB.bitfields)
var/list/flags = list()
Expand Down Expand Up @@ -130,3 +188,8 @@
</table></td><td class='rbrak'>&nbsp;</td></tr></tbody></table></span>"} //TODO link to modify_transform wrapper for all matrices

#undef VV_HTML_ENCODE
#undef STYLE_READ_ONLY
#undef STYLE_NORMAL
#undef STYLE_LIST
#undef STYLE_SPECIAL
#undef STYLE_EMPTY
44 changes: 28 additions & 16 deletions code/modules/admin/view_variables/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,37 @@
if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list))
return
var/target = GET_VV_TARGET
var/vv_refresh_target /// If this var has a reference, vv window will be auto-refreshed

vv_do_basic(target, href_list, href)
if(istype(target, /datum))
var/datum/D = target
D.vv_do_topic(href_list)
// for non-standard special list
if(href_list["dmlist_origin_ref"])
var/datum/located = locate(href_list["dmlist_origin_ref"])
var/dmlist_varname = href_list["dmlist_varname"]
if(!isdatum(located) || !GLOB.vv_special_lists[dmlist_varname] || !(dmlist_varname in located.vars))
return
if(GET_VV_VAR_TARGET || href_list[VV_HK_DO_LIST_EDIT]) // if href_list["targetvar"] exists, we do vv_edit to list. if not, it's just viewing.
vv_do_list(located.vars[dmlist_varname], href_list)
GLOB.vv_ghost.mark_special(href_list["dmlist_origin_ref"], dmlist_varname)
vv_refresh_target = GLOB.vv_ghost
// for standard /list
else if(islist(target))
vv_do_list(target, href_list)
if(href_list["Vars"])
var/datum/vars_target = locate(href_list["Vars"])
if(href_list["special_varname"]) // Some special vars can't be located even if you have their ref, you have to use this instead
vars_target = vars_target.vars[href_list["special_varname"]]
debug_variables(vars_target)
GLOB.vv_ghost.mark_list(target)
vv_refresh_target = GLOB.vv_ghost
// for standard /datum
else if(istype(target, /datum))
var/datum/D = target
D.vv_do_topic(href_list)

// if there is no `href_list["target"]`, we check `href_list["Vars"]` to see if we want see it
if(!target && !vv_refresh_target)
vv_refresh_target = locate(href_list["Vars"])
// "Vars" means we want to view-variables this thing.

if(vv_refresh_target)
debug_variables(vv_refresh_target)
return

//Stuff below aren't in dropdowns/etc.

Expand Down Expand Up @@ -122,11 +142,3 @@
log_admin(log_msg)
admin_ticket_log(L, "<font color='blue'>[log_msg]</font>")
vv_update_display(L, Text, "[newamt]")


//Finally, refresh if something modified the list.
if(href_list["datumrefresh"])
var/datum/DAT = locate(href_list["datumrefresh"])
if(istype(DAT, /datum) || istype(DAT, /client) || islist(DAT))
debug_variables(DAT)

2 changes: 1 addition & 1 deletion code/modules/admin/view_variables/topic_basic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
if(!target)
to_chat(usr, "<span class='warning'>The object you tried to expose to [C] no longer exists (nulled or hard-deled)</span>")
return
message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a <a href='?_src_=vars;datumrefresh=[REF(target)]'>VV window</a>")
message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a <a href='?_src_=vars;Vars=[REF(target)]'>VV window</a>")
log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]")
to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
C.debug_variables(target)
Expand Down
17 changes: 17 additions & 0 deletions code/modules/admin/view_variables/topic_list.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
//LISTS - CAN NOT DO VV_DO_TOPIC BECAUSE LISTS AREN'T DATUMS :(
/client/proc/vv_do_list(list/target, href_list)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Advanced ProcCall detected - You shouldn't call /vv_do_list() directly.</span>")
return
var/target_index = text2num(GET_VV_VAR_TARGET)
if(check_rights(R_VAREDIT))
var/dmlist_varname = href_list["dmlist_varname"]
if(dmlist_varname)
var/dmlist_secure_level = GLOB.vv_special_lists[dmlist_varname]
if(isnull(dmlist_secure_level)) // href protection to make sure
log_admin("[key_name(src)] attempted to edit a special list ([dmlist_varname]), but this doesn't exist.")
return
else if(dmlist_secure_level == VV_LIST_EDITABLE)
log_world("### vv_do_list() called: [src] attempted to edit a special list ([dmlist_varname]) Security-level:[dmlist_secure_level](allowed)")
log_admin("[key_name(src)] attempted to edit a special list ([dmlist_varname]) Security-level:[dmlist_secure_level](allowed)")
else // fuck you exploiters
log_world("### vv_do_list() called: [src] attempted to edit a special list ([dmlist_varname]), but denied due to the Security-level:[dmlist_secure_level]")
log_admin("[key_name(src)] attempted to edit a special list ([dmlist_varname]), but denied due to the Security-level:[dmlist_secure_level]")
message_admins("[key_name_admin(src)] attempted to edit a special list ([dmlist_varname]), but denied due to the Security-level:[dmlist_secure_level]. Bonk this guy.")
return
if(target_index)
if(href_list[VV_HK_LIST_EDIT])
mod_list(target, null, "list", "contents", target_index, autodetect_class = TRUE)
Expand Down
Loading

0 comments on commit 72350f6

Please sign in to comment.