Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Last active October 7, 2022 23:06
Show Gist options
  • Save JamoCA/7e544f1488a703d868cecc2b7ae5c2ed to your computer and use it in GitHub Desktop.
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.
<!--- 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