Client Side GlideRecord Security

 

This bug had me scratching my head and pulling my hair out for a good couple of days before I finally realised there must be a bug. Let me explain….

Server side GlideRecords do not adhere to ACLs. For example, if you have a write rule on a field which prevents anyone from writing to this field, if a UI action attempted to write to this field using a server side GlideRecord it would work just fine.

This is intended as an end user cannot run custom GlideRecords themselves so any attempt at any CRUD (create, read, update and delete) operation is actually intended by the ServiceNow implementer.

As anyone can write a custom client side GlideRecord using any browsers javascript console, client side GlideRecords must follow and evaluate all CRUD rules. So far so good right? Wrong! CRUD rules on client side GlideRecords aren’t evaluated quite right!

The security is checked too early which, in certain scenarios that I’ll shortly explain, will refuse access to the operation.

If the operation you are trying to complete has any contextual ACLs applied to it, access will always be denied, regardless of whether right or wrong.

How to reproduce the issue:

  • Create a Delete rule on the Incident table with the condition ‘Active = true’. ie, only allow access to delete an incident if it is active
  • Now find an active incident and copy down the number (Number x).
  • Press CTRL+ALT+J to open the javascript editor and run the following script:

var gr = new GlideRecord(‘incident’);

gr.addQuery(‘number’, ‘number x‘);

gr.query();

if (gr.next()) {

gr.deleteRecord();

}

  • Now go search for that record and you’ll find it has not been deleted

So what’s happening:

Essentially, when doing a client side GlideRecord, an AJAX call is made which triggers a server side GlideRecord and this is where it seems the issue is.

The GlideRecord which you performed sent on the client side GlideRecord, is duplicated in the server side GlideRecord with one difference… it checks security. But it seems like it checks it too early. If we continue with the above example, the server side GlideRecord must look like this (or something very similar):

var gr = new GlideRecord(‘incident’);

gr.addQuery(‘number’, ‘number x‘);

gr.query();

if (!gr.canDelete() )

return;

if (gr.next()) {

gr.deleteRecord();

}

 See what happened there? The canDelete() function is called before the record has been retrieved, so any contextual ACL will always fail because the field doesn’t exist. In our example, it’s checking if ‘Active = true’, but ‘Active’ is neither true nor false!

The good news

It’s rather rare that you’d need to do a client side GlideRecord for this sort of stuff, and in fact, it’s not recommended. I’d recommend using a GlideAjax and performing the GlideRecord asynchronously to avoid any performance impacts on the client. Secondly, and more importantly, this security implication forces it to be over-zealous rather than allowing access to things that should be locked away so you know your system is still safe!

Hopefully you found this useful and saves you from some of the frustration I had when trying to figure this out.

2 Comments

  1. Recently found your site and have been enjoying reading your past articles. While I agree with you above that GlideAjax should be leveraged on the client for performance reasons, for most GlideRecord use cases there’s actually a simpler OOB (though undocumented) way of doing this that’s the best of both worlds for many common use cases.

    For example, the primary reason not to use GlideRecord client side “for performance reasons” is that when you use a GlideRecord you typically are only interested in one or two fields (such as to get a user’s phone number), not the entire record. But if you use GlideRecord naïvely you’ll get all the fields all the time. The solution is to use the setDisplayFields API:

    var gr = new GlideRecord(‘sys_user’);
    gr.addQuery(‘sys_id’, caller_id);
    gr.setDisplayFields([‘phone’, ‘manager.sys_id’, ‘manager.phone’]);
    gr.query();

    ….

    Notice that you can also dot-walk here; a nice bonus. Instead of getting the full record returned, you only get the specific attributes you requested with setDisplayFields. With this approach you get the convenience and ease of use of GlideRecord with the performance benefits of GlideAjax.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s