ServiceNow ACLs Explained: A Complete Security Reference

How Access Control Lists actually work in ServiceNow — evaluation order, common patterns, debugging access issues, and the mistakes that create security vulnerabilities.

ACLs (Access Control Lists) are the primary security mechanism in ServiceNow. Understanding how they evaluate is essential for both building secure applications and debugging access issues efficiently.

How ACL Evaluation Works

When a user attempts to perform an operation on a record or field, ServiceNow evaluates ACLs in a specific order. Understanding this order explains why some ACLs seem to be ignored.

The evaluation sequence:

  1. ServiceNow finds all ACLs that match the object being accessed
  2. ACLs are sorted by specificity — more specific ACLs evaluate before general ones
  3. The first matching ACL that grants or denies access determines the outcome
  4. If no ACL matches, access is denied by default (secure by default)

ACL Types

ACLs can be applied at different levels:

  • Table-level — controls read/write/create/delete on the entire table
  • Field-level — controls read/write on a specific field
  • Record-level — controls access based on record data (uses scripts)

The Three Conditions

Each ACL has three conditions that must ALL pass for access to be granted:

  1. Role — user must have one of the specified roles
  2. Condition — a filter condition evaluated against the record
  3. Script — a server-side script that returns true or false

If any one of these conditions fails, the ACL denies access regardless of the others.

Common Pattern: Record Owner Access

// ACL Script: Allow access only to the record's caller
// Table: incident, Operation: write
(function() {
    // Allow if user is the caller on the incident
    if (current.caller_id == gs.getUserID()) {
        return true;
    }
    // Allow if user has itil role
    if (gs.hasRole('itil')) {
        return true;
    }
    return false;
})();

Debugging Access Issues

The fastest way to debug ACL issues is using the Security Debug plugin:

  1. Navigate to System Diagnostics > Security
  2. Enter the sys_id of the record the user cannot access
  3. Enter the user's sys_id
  4. Select the operation (read, write, etc.)
  5. Click Evaluate

The output shows exactly which ACL evaluated, in what order, and why access was granted or denied.

Alternatively, add &sysparm_ck_security=true to any URL to see ACL debug output directly on the page.

Common Security Mistakes

1. Over-broad roles in ACLs

Using the admin role as a bypass in ACL scripts is common but dangerous. If admin access is ever expanded, those ACLs expose data you didn't intend to expose.

2. Missing field-level ACLs

Securing the table but not the fields means a user with read access to the table can see all fields including sensitive ones like salary, SSN, or passwords. Always add field-level ACLs for sensitive data.

3. ACL scripts with performance issues

ACL scripts execute on every record access. A script that queries another table runs that query for every single record in a list view. Use gs.hasRole() and current field comparisons rather than GlideRecord queries inside ACL scripts.

4. Not testing as the actual user

Testing ACLs as admin with impersonation is unreliable because admin bypasses many ACL checks. Test in a separate browser session logged in as the actual user or a test account with the exact same roles.

Best Practices

  • Create custom roles for your applications rather than using built-in roles
  • Document what each custom role grants — add a description to the role record
  • Review ACLs during every release — they accumulate technical debt fast
  • Never use admin checks in ACL scripts for production access control
  • Test ACL changes in a sub-production environment before promoting

Scripted ACLs — when and how to use them

Most ACLs can be configured using the condition builder without scripting. Use scripted ACLs (the Script field on the ACL record) only when the access condition requires logic that the condition builder cannot express — checking a field on a related record, evaluating a complex multi-condition involving both user attributes and record attributes, or running a calculation that determines access. The script must set answer = true to grant access or answer = false to deny it. The script runs every time the ACL is evaluated — performance of scripted ACLs matters significantly on high-traffic tables. Avoid GlideRecord queries inside ACL scripts if at all possible; use gs.hasRole() and current field value comparisons instead.

// Scripted ACL: grant read access only if record belongs to user's department
var userDept = gs.getUser().getDepartmentID();
var recordDept = current.getValue('u_department');
answer = (userDept == recordDept) || gs.hasRole('admin');

Data policies vs ACLs

Data policies enforce field-level constraints regardless of how a record is modified — through the UI, via REST API, or through a script. ACLs govern access to records and fields through the platform UI and REST APIs. A field marked mandatory in a data policy cannot be saved empty regardless of ACL configuration. Use data policies for data integrity rules that must hold everywhere; use ACLs for access control rules that govern who can read or modify. The two systems are complementary, not redundant. See ACLs complete guide and roles vs groups.

ACL Evaluation Order and Hierarchy

ServiceNow evaluates ACLs in a specific hierarchy that determines which rule applies when multiple rules could match a given operation. The evaluation order is: field-level ACLs first, then table-level ACLs, with more specific rules taking precedence over less specific ones. A field ACL for incident.caller_id takes precedence over a table ACL for incident. Within the same specificity level, ACLs are evaluated in order and access is granted as soon as any ACL is satisfied — the engine does not continue checking once a matching rule grants access. Understanding this means that adding a permissive field ACL can bypass a restrictive table ACL for that specific field, which is the intended design for implementing exceptions to general access rules.

Scripted ACLs and Performance

When an ACL has a condition script, that script runs for every record evaluation. On a list view returning 20 records, a scripted ACL runs 20 times. On a report or export returning 10,000 records, it runs 10,000 times. This is why complex logic in ACL scripts — especially anything involving GlideRecord queries — can devastate instance performance. The GlideRecord performance guide applies directly to ACL scripting: never run unbounded queries, never run queries inside loops, and use gs.hasRole() checks before any database access since role checks are fast in-memory operations. Cache the result of expensive lookups in the session context if the same calculation will be needed across multiple ACL evaluations in the same request.

Table-Level vs Row-Level Security

Standard ACLs provide table-level security: a user either has access to the Incident table or they do not. Row-level security — where a user can see some incident records but not others — requires additional ACL scripting or the use of Data Policies. The common pattern for row-level security is an ACL script that checks a relationship between the current record and the current user: does the record's assigned_to field match gs.getUserID()? Is the user a member of the record's assignment group? These scripts must be written carefully to avoid performance problems, as noted above. For complex row-level security requirements, evaluate whether the full ACL documentation pattern or a Data Policy approach better fits the use case.

ACLs in Scoped Applications

ACLs in a scoped application operate within the scope context. A scoped ACL can protect tables and fields within its own scope, but cannot modify ACL behaviour for global or other scoped tables. This isolation means that deploying a scoped app cannot accidentally break ACLs on the global Incident or Change table — an important safety property for commercial scoped app development. The tradeoff is that scoped apps cannot implement broad security policies for shared platform tables. For organisations trying to enforce consistent access policies across multiple scoped apps, a shared security scoped app that other apps declare as a dependency provides a central place to manage common role definitions and ACL patterns. The roles and groups guide covers the role management side of this architecture.

Testing and Validating ACL Changes

ACL changes are among the highest-risk configuration changes in ServiceNow — a misconfigured ACL can expose sensitive data to unauthorised users or lock authorised users out of critical functionality. Testing protocol should include explicit verification of both "should have access" and "should not have access" scenarios, using impersonation to test as specific user roles. The Access Control Simulator (accessible via the Security module) lets you test what ACL evaluation result a given user-table-operation combination would produce, showing which rule granted or denied access and why. Run the simulator against your change before promoting it, and include ACL verification as a step in your Instance Scan checks for the affected tables.

Default Deny vs Default Allow

ServiceNow's security model is default-deny when ACLs are enabled: if no ACL explicitly grants access to a table or field, access is denied. This is the secure default, but it creates friction during initial deployment when developers discover that new tables need explicit ACLs before any user can access them. The common shortcut of adding a permissive ACL that grants access to everyone with any role in the platform is a security anti-pattern that is easy to introduce under time pressure and difficult to audit later. Instead, define the access model for new tables at the design stage — which roles need read, write, create, and delete access — and create the ACLs before the table goes to any non-admin user. The roles and groups guide covers how to structure roles to make ACL assignment maintainable at scale.

ACL Troubleshooting with the Security Debugger

The ServiceNow Security Debugger (enable via security_log_debug=true in the URL or through System Diagnostics) logs every ACL evaluation decision during a session. When a user reports they cannot see a record or a field they should be able to access, the Security Debugger shows exactly which ACL rule denied access and why — the rule name, the condition that was evaluated, and whether a script was involved. This eliminates the guesswork of reviewing dozens of ACL records to find which one is causing an issue. Disable the debugger after troubleshooting, as it generates significant log volume. For systematic ACL audits rather than individual incident investigation, the Access Control Simulator provides the same evaluation trace without the performance overhead of full debug logging. Combine both tools: the simulator for testing planned ACL configurations, the debugger for diagnosing reported access issues in production.

Want the complete reference?

This article is part of the NowSpectrum knowledge library. Browse all products for cheat sheets, interview prep, and deep-dive reference guides.

Browse All Products →
← Back to all posts