Last active
October 7, 2022 23:06
-
-
Save JamoCA/7e544f1488a703d868cecc2b7ae5c2ed to your computer and use it in GitHub Desktop.
This getScopedVariable UDF tests to determine if a valid scope + variable exists w/o throwing an error. If non-existent, a default value is returned.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--- 2022-10-07 getScopedVariable UDF by James Moberg / SunStar Media | |
GIST: https://gist.github.com/JamoCA/7e544f1488a703d868cecc2b7ae5c2ed | |
BLOG: https://dev.to/gamesover/safely-fetching-scoped-variables-while-avoiding-scope-injection-2ee3 | |
TWEET: https://twitter.com/gamesover/status/1578522152600424449 | |
This getScopedVariable UDF tests to determine if a valid scope + variable exists w/o throwing an error. | |
If non-existent, a default value is returned. ---> | |
<cfscript> | |
// This is only test code. | |
// The reason for this is to 1) attempt to validate scoped variables when session, client or application variables aren't enabled | |
// and 2) return a fallback variable if non-existent (instead of throwing an error) | |
// ACF 2016+ has application this.searchImplicitScopes setting | |
// Railo/Lucee has applications this.scopeCascading="strict" setting | |
// More info on Scope Injection and scopes that are affected | |
// https://www.petefreitag.com/item/834.cfm | |
// https://helpx.adobe.com/coldfusion/developing-applications/the-cfml-programming-language/using-coldfusion-variables/about-scopes.html | |
function getScopedVariable(required string name, required string scope="application", any default="") { | |
local.scopeClassMap = [ | |
"application": ["ApplicationScope", "ApplicationImpl"] | |
,"form": ["FormScope", "FormImpl"] | |
,"url": ["UrlScope", "URLImpl"] | |
,"session": ["SessionScope", "sessionMemory"] | |
,"client": ["NoOperClientScope", ""] | |
,"server": ["ProtectedScope", "ServerImpl"] | |
,"cookie": ["CookieScope", "CookieImpl"] | |
,"cgi": ["CgiScope", "CGIImpl", "CGIImplReadOnly"] | |
]; | |
local.variable = {}; | |
switch(lcase(arguments.scope)) { | |
case "application": | |
local.subType = application?.getClass()?.getName(); | |
local.variable = application; | |
break; | |
case "form": | |
local.subType = form?.getClass()?.getName(); | |
local.variable = form; | |
break; | |
case "url": | |
local.subType = url?.getClass()?.getName(); | |
local.variable = url; | |
break; | |
case "session": | |
local.subType = session?.getClass()?.getName(); | |
local.variable = session; | |
break; | |
case "client": | |
try { | |
local.subType = client?.getClass()?.getName(); // Lucee bug | |
if (local.keyexists("subType")) local.variable = client; | |
} catch (any e){}; | |
break; | |
case "server": | |
local.subType = server?.getClass()?.getName(); | |
local.variable = server; | |
break; | |
case "cookie": | |
local.subType = cookie?.getClass()?.getName(); | |
local.variable = cookie; | |
break; | |
case "cgi": | |
local.subType = cgi?.getClass()?.getName(); | |
local.variable = cgi; | |
break; | |
default: | |
return arguments.default; | |
break; | |
} | |
// return fallback if subtype doesn't exist or doesn't match classname | |
if (!local.keyexists("subType") || !arrayfind(local.scopeClassMap[arguments.scope], listlast(local.subType,"."))) { | |
return arguments.default; | |
} | |
// return fallback if key does not exist in the scoped object | |
if (!listfindnocase(structkeylist(local.variable), arguments.name)){ | |
return arguments.default; | |
} | |
return local.variable[arguments.name]; | |
} | |
// establish a variable name | |
testVarName = "randomVarName_#gettickcount()#"; | |
// inject variables into common scopes that are searched | |
url.application = [ | |
"#testVarName#": ["url scope"] | |
]; | |
form.application = [ | |
"#testVarName#": ["form scope"] | |
]; | |
writedump(var=url, label="url (w/injected scope-named key)"); | |
writedump(var=form, label="form (w/injected scope-named key)"); | |
// output "application" variable. (This application variable doesn't exist and shouldn't be dumped.) | |
try { | |
writedump( var=application[testVarName], label="application.#testVarName# (unsafe)"); | |
} catch (any e) { | |
writeoutput("<div>Error: Application scope or '#testVarName#' doesn't exist.</div>"); | |
} | |
// use function to validate scoped variable and return fallback if it doesn't exist. | |
result = getScopedVariable(name=testVarName, scope="application", default=["default fallback"]); | |
writedump( var=result, label="getScopedVariable"); | |
// Server scope fallback test | |
// server[testVarName] = [now()]; | |
// if scoped variable (that isn't searched) doesn't exist, an error is thrown. | |
try { | |
writedump( var=server[testVarName] , label="server.#testVarName# (unsafe)"); | |
} catch (any e) { | |
writeoutput("<div>Error: server scope or '#testVarName#' doesn't exist.</div>"); | |
} | |
// use function to validate scoped variable and return fallback if it doesn't exist. | |
result = getScopedVariable(name=testVarName, scope="server", default=[now()]); | |
writedump( var=result, label="getScopedVariable"); | |
// structdelete(server, testVarName); | |
</cfscript> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment