ACL evaluation for server-side and client-side scriptsIssue <!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } This article explains when Access Control Rules (ACLs) are evaluated for Business Rules, UI Scripts, Script Includes, background scripts, UI Actions, and Client Scripts at runtime. When working with Business Rules, UI Scripts, Script Includes, background scripts, UI Actions, and Client Scripts, it is important to understand when ACLs are evaluated at runtime. A lack of understanding of this behaviour is a common source of frustration when investigating issues related to client-side and server-side JavaScript. Three key facts apply: All scripts mentioned above run in the context of the current user.Scripts that use the ServiceNow server-side JavaScript API classes to create, read, update, and delete records are not subject to passing or failing ACLs. The GlideRecordSecure class is the exception.Scripts that use the ServiceNow client-side JavaScript API functions always observe ACLs. Release<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } All releases Resolution<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Approving records without write access to the State field A single UI Action demonstrates all three facts above. The UI Action can be viewed at the following URL on a test instance: https://instance-name.service-now.com/nav_to.do?uri=sys_ui_action.do?sys_id=8468ee55c611227d01a072a67bdbd3e7 The Client checkbox on this UI Action is not checked, meaning it runs server-side JavaScript. The script sets the state of the current record to "approved" and then updates the record. Because ACLs are not observed on the server side by default, any user with access to the UI Action form button can approve the current approval record regardless of whether they pass or fail the ACLs on table sysapproval_approver. The condition to see the UI Action form button requires that the current record's state is "requested" and that the current user is either the approver on the record or a delegate of the approver. Problems occur when the condition is relaxed to allow more end users to see the button, on the assumption that ACLs on the table will prevent approval by users without write permissions. Test 1: Server-side JavaScript ignores ACLs by default Change the condition on the UI Action from: current.state == 'requested' && isApprovalMine(current) to: current.state == 'requested' Search for Debug in the Application Navigator and select Debug Security. Impersonate the ITIL user. The ITIL user can read all records on table sysapproval_approver but will not pass the write ACL for the State field on a base system instance.Navigate to: https://instance-name.service-now.com/nav_to.do?uri=sysapproval_approver_list.do Open any record where the state is "requested".Search the browser page for the text "state/write". On a Mac, search in Chrome with ⌘+Enter. On Windows, use Ctrl+F. The results confirm that the ITIL user fails the write ACL for sysapproval_approver.state.Select the Approve UI Action button. Although ACLs might be expected to prevent the ITIL user from writing to the State field, the server-side JavaScript in the UI Action updates the State field without checking ACLs. The record is approved by a user that cannot pass the write ACL for the State field, because that user was given access to the Approve UI Action form button. Options for enforcing ACLs in server-side scripts To enforce ACLs in server-side scripts, use one of the following options: Use the canCreate(), canRead(), canWrite(), and canDelete() functions of the server-side GlideRecord class, or the canRead() and canWrite() functions of the server-side GlideElement class. Use the GlideRecord functions to check ACLs at the table level and the GlideElement functions to check ACLs at the field level. These functions are straightforward — they return true if the user can perform the action and false if they cannot.Use the GlideRecordSecure class. This class works the same way as GlideRecord except that it observes ACLs on the table it is reading from or writing to. Test 2: Enforcing ACLs with canWrite() Confirm the condition on the UI Action is still: current.state == 'requested' Change the script from: current.state='approved'; current.update(); to: if(current.state.canWrite()){ current.state='approved'; current.update(); } Save the changes to the UI Action, impersonate the ITIL user, and try to approve a request on table sysapproval_approver. The State field no longer changes from "Requested" to "Approved". The canWrite() function of the GlideElement class checks whether the ITIL user can pass the write ACLs on sysapproval_approver.state before allowing any update. The user fails the ACL check and cannot change the State field. Test 3: Enforcing ACLs with GlideRecordSecure Confirm the condition on the UI Action is still: current.state == 'requested' Change the script from: if(current.state.canWrite()){ current.state='approved'; current.update(); } to: var grs = GlideRecordSecure('sysapproval_approver'); grs.get(current.sys_id); grs.state='approved'; grs.update(); Save the changes to the UI Action, impersonate the ITIL user, and try to approve a request on table sysapproval_approver. The result is the same as Test 2 — the ITIL user cannot change the State field. The technique differs but the outcome is identical. How client-side JavaScript handles ACL enforcement Client-side JavaScript API functions always observe ACLs because there is no control over who runs them. Any technically proficient end user can open the JavaScript console in the browser and run any client-side JavaScript. This can be demonstrated by changing the previous UI Action to run client-side JavaScript, using the client-side GlideRecord class to set the State field to "Approved" and update the record. Test 4: Client-side JavaScript observes ACLs automatically Confirm the condition on the UI Action is still: current.state == 'requested' Check the Client checkbox.Populate the Onclick field with: approve() Change the script from: var grs = GlideRecordSecure('sysapproval_approver'); grs.get(current.sys_id); grs.state='approved'; grs.update(); to: function approve(){ var gr = new GlideRecord('sysapproval_approver'); gr.get(g_form.getUniqueValue()); gr.state = 'approved'; gr.update(); } Save the changes to the UI Action, impersonate the ITIL user, and try to approve a request on table sysapproval_approver. No special effort is required to ensure ACLs are observed. The ServiceNow client-side JavaScript APIs always observe ACLs. Understanding these differences is key to resolving issues related to the ServiceNow JavaScript API. Related Links<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } GlideRecord - Scoped Using GlideRecordSecure