ServiceNow Cross-Scope Access to Private Members
Javier Arroyo
Posted on March 20, 2021
TL;TR
Export/Import a Script Include from/to scopes to achieve cross-scope access of private Script Includes without having to change their Accessibility to "All application scopes".
Origin of this post
A cross-scope accessibility question about executing private members of one scope from inside another was raised in sndev.slack.com #codereview channel. The desire wasn't as much as to call a single script include, but to be able to call any script include without having to edit its accessibility rights. Leave OOB artifacts intact yet, still be able to use them from other scopes.
When a private Script Include is called from outside its scope, “Evaluator: java.lang.SecurityException: [Method Name] is not allowed in scoped applications” is thrown. For those working in Scopes, it becomes terribly irritating and limiting. It's either edit OOTB artifacts, or throw a hail-marry with extension points. These is no mechanism to "wholesale" use private script includes from any scope. Should there be? That can be a good debate.
I do not clearly recall who raised the question, my apologies if mistaken. It could have been Tim Woodruff. He sought a proven and tested method to bypass platform restrictions (naughty boy).
While I have never experienced any cross-scope security restrictions from any of the strategies I’ve built through the years, it doesn’t necessarily mean that the approaches were/are viable. Public scrutiny is most welcomed.
The SecurityException is raised when executing a Script Include that has outside the containing scope.
The private script include below is an example of a script that will generate a SecurityException; notice field Accessible from is set to This application scope only.
Side stepping the exception, from where to derived the solution
Early on when the platform introduced scopes and applications, the strategy of "Jump" Scripts to expose private members outside a scope was also born. Methods inside the "Jump" script delegated to to private Script Includes inside the same scope. The platform indirectly advertised the solution to cross-scope security exceptions. It really doesn't care how a private member is executed as long as its done so through a public member belonging to the same scope.
This meant that exporting a public SI with a more suitable delegation strategy would allow execution of private members to other scopes. The restriction remains that the caller must be public and part of the source scope but, the open door is enough. All that's needed is little digging in.
Side note
If you are going to use the approaches shown here, also use some sort of governance for object composition to track who is using what.
My provided approach in sndev Slack
From the source scope, export a utility function that when supplied arguments will call a private, or not, Script Include.
Script Include ExportGlobalAccess has field Accessible from set to All application scopes. It is an IIFE that returns a curried function. During the first execution it's supplied 2 arguments; the script include name, and the script include parameter. It's second execution needs to be supplied with the method name in the script include and the argument to use. This fancier "jump" script has room for improvement; such as support for n-ary, and non-constuctor functions. But, it's a way to call any OOTB Script Include from any scope.
To use it from a target scope
global.ExportGlobalAccess(‘SIName’,’SIParam’)(‘SIMethodName’,’SIMethodParam’)
An import SI to decrease verbosity and introduce flexibility is always helpful. All it does is call the exports Script Include from the target scope. Such as:
Importing global.ExportGlobalAccess example
Using the shortcut SI from the target scope looks like this
GlobalScope(‘SIName’,’SIParam’)(‘SIMethodName’,’SIMethodParam’);
Second Approach
Instead of a exporting a utility function from the source scope, “Register” script includes to be mixed into a "public" API.
The “registration” mechanism is a sys_property 😊 to store the names of the script includes to export as a comma delimited string.
Then some script includes purely for POC
The Exports Script Include
Uses the sys_property to determine which SIs to include in the API.
The output of Exports:
{
foo: function (value){},
bar: function (value){},
baz: function (value){}
}
When more names are added to the sys_properties, they'll show up in the exported API.
Next. Imports the exports SI into a target scope
Usage:
Call the functions directly, or in a pipe.
gs.debug( pipe([ImportsMess.foo, ImportsMess.bar, ImportsMess.baz])('javier'));
Summary:
By exporting a Script Include from a source scope then importing it to another scope, private members can be executed cross scope. Those displayed in this article are not the only strategies available, it is entire up to the implementer.
Happy Snowing
Posted on March 20, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.