DEV Community

Marek Krčma
Marek Krčma

Posted on

ServiceNow: 1 thing for safer GlideRecord scripts

Many task-based tables use parent-child relationship and run logic: to do something on child tasks, or roll-up something into parent. Imagine you do some stuff on child incidents like state change, update work notes (by business rule, event, flow, flow action) where parent is some sys_id.

You pass parent incident, but this is the pain point: when testing it, you are sure that you pass sys_id. But step out and think in this way:
What if the parameter is empty or null value is pushed?

And actually this might happen pretty easily. For example when event parameter is not initialized, or flow action returns null.

I will show the wrong code later, so this is the correct code with if condition which checks the variable parent record sys_id. Pain point is explained below.

/*
* Get child incidents for a given parent incident and update comments
* sys_id of parent incident is pushed by argument (event parm, sub flow, scheduled job, script include) 
* and sets sysIdParent variable
*/

// (1) var sysIdParent = '';
// (2) var sysIdParent = null;
// (3) var sysIdParent = undefined;
// (4) var sysIdParent = '14fef3c7878230105d8afea9cebb3594';

var sysIdParent = null; // sys_id of parent incident
// This IF checks sysIdParent variable to avoid null/empty/undefined
if (sysIdParent != '' && sysIdParent != null) {
    var gr_ChildIncidents = new GlideRecord('incident');
    gr_ChildIncidents.addQuery('active', true);
    gr_ChildIncidents.addQuery('parent_incident', sysIdParent);
    gr_ChildIncidents.query();
    gs.log(gr_ChildIncidents.getRowCount());
    while (gr_ChildIncidents.next()) {
        gr_ChildIncidents.comments = 'Child incident triggered from script.';
        gr_ChildIncidents.update();
    }
}
else {
    gs.log('sysIdParent is null/empty/undefined');
}

Enter fullscreen mode Exit fullscreen mode

You expect the real sys_id is pushed (case 4 in the script). What will happen, if it is passed null value here gr_ChildIncidents.addQuery('parent_incident', sysIdParent); and there is no if condition checking input parameters? Logic assumption is that nothing happens. It is null so GlideRecord will not fetch any data. Wrong!

Well, the script will execute for all active parent incidents (field parent incident is empty/null). It can be large number, instead of 3 child incidents… This is not good. Imagine there is deleteRecord() instead of update. You can simulate it on your personal development instance and see how many incidents are affected by script (wrong code):

// Instance has active 10K parent incidents, 
// 3 child incidents related to parent with sys_id: 14f...594
// (1) var sysIdParent = '';
// (2) var sysIdParent = null;
// (3) var sysIdParent = undefined;
// (4) var sysIdParent = '14fef3c7878230105d8afea9cebb3594';

var sysIdParent = null; // sys_id of parent incident
// sysIdParent variable is not checked and null value will cause
// unexpected behaviour in GlideRecord query
var gr_ChildIncidents = new GlideRecord('incident');
gr_ChildIncidents.addQuery('active', true);
gr_ChildIncidents.addQuery('parent_incident', sysIdParent);
gr_ChildIncidents.query();
// Number of affected rows
gs.log(gr_ChildIncidents.getRowCount());

// Number of affected records:
// (1) 10 000
// (2) 10 000
// (3) 10 000
// (4) 3
Enter fullscreen mode Exit fullscreen mode

If you come from Java world or any strict object language you are learned to check the input parameters. Do the same when dealing with GlideRecord. Check parameters used in GlideRecord queries, it will prevent your stress level from reaching level billion.

I explained the issue on incident table, but the same issue is valid for other use cases where you use GlideRecord in scripts. Consequences on cmdb could be serious.

Is null nothing in JavaScript?

The value null represents the intentional absence of any object value. It is one of JavaScript's primitive values.

I would use typeof operator. The typeof operator returns a string indicating the type of the operand's value. But check this and run on your personal development instance:

gs.log(typeof 10);      // number
gs.log(typeof '10');    // string
gs.log(typeof true);    // boolean
gs.log(typeof null);    // object (wait?!)
Enter fullscreen mode Exit fullscreen mode

typeof null

typeof null returns object in JavaScript… But it was said that null is primitive type. This sounds like singularity in JavaScript world. Well, this is not issue related to ServiceNow, there is a bug in core JavaScript since the beginning unfortunately:

https://javascript.plainenglish.io/there-is-a-bug-in-javascript-since-day-one-typeof-null-9b18da349cc6

How to check variables to avoid empty/null/undefined?
So typeof will not help you. Instead, I use conditions
sysIdParent != '' && sysIdParent != null
which check that variable is not empty / null / undefined.

null == undefined

If you are curious more about null value in JavaScript, check this post, it goes deeper: https://javascript.plainenglish.io/how-to-check-for-null-in-javascript-dffab64d8ed5


It is easy to write 15 lines GlideRecord script which is working on the first sight. But the devil is always looking for opportunity. Step out and always think about possible situations: What if null, undefined, false, empty string is passed as parameter? What if previous action like flow, subflow ends with error? Did I treat these errors?

These whatifs will help you to analyze and step back to see the wider perspective. And then write safer code. Take time to analyze what are the input parameters, where do they come from. And take time to test it on a dev instance.

I am curious to hear about some "fun dev stories when null is passed" in comments :)

Latest comments (0)