The audit finding was specific: a non-privileged user could read salary fields on sys_user because of an ACL written three years ago that nobody owned. The fix took an hour; the explanation to the auditor took a week. ACL evaluation in ServiceNow is well-documented and consistently misunderstood — the patterns below cover what actually happens at evaluation time and how to keep the model auditable.
How ACL Evaluation Works
ACLs evaluate in a specific order: table-level first, then field-level, then record-level (sometimes called “row-level” through query business rules or the query operation). Multiple matching ACLs at the same level are combined permissively — access is granted if any matching ACL permits and none deny via condition. The most consequential subtlety: if no table-level ACL exists for an operation, no downstream ACL will save you; the absence of a table-level rule is a deny by default in modern releases.
Evaluation order for a read on incident.priority:
1. table-level ACL on incident (read)
2. field-level ACL on incident.priority (read)
3. record-level ACL on this incident (read, via query rules)
Each must permit. Permissive within level, restrictive across levels.
The Wildcard Problem
A wildcard ACL with table='*' and field='*' is convenient at write time and dangerous forever after. It applies to everything, including future tables that did not exist when the ACL was written. Audit for wildcards quarterly and replace with scoped rules. The only legitimate wildcards are platform-default ACLs that ship with the system; custom wildcards are findings.
Roles vs ACLs
Roles answer “who”; ACLs answer “what.” A common mistake is over-granting the admin role “just to test” something and forgetting to revoke it. Build roles that map to real personas (operations engineer, change approver, HR business partner) and assign ACLs to those roles. The role catalog should have a one-line description per role explaining what it grants and to whom; roles without descriptions accumulate cruft.
// Helper: list all ACLs granting a specific role
var gr = new GlideRecord('sys_security_acl_role');
gr.addQuery('sys_user_role.name', 'finance_reader');
gr.query();
while (gr.next()) {
gs.print(gr.sys_security_acl.name + ' ' + gr.sys_security_acl.operation);
}
Regulated Environments
SOX, HIPAA, GDPR, and similar frameworks all require ACL documentation at audit time. Export ACL definitions quarterly via update set or scripted export. Tag sensitive fields with classification metadata (PII, PHI, financial). Build Data Certification processes that periodically verify the ACL still matches the documented policy — the policy on paper and the rules in the system drift over years if nobody checks.
Testing ACLs
Impersonate a user with the expected role set and walk through the tasks the role should perform. Any “number does not match” message, silent missing data on a form, or empty list view that should have results indicates an ACL gap — not a UI bug. Testing as admin proves nothing about the security model; the test must run as a non-privileged user. Build the impersonation walkthrough into change review for any ACL change.
Common Failure Modes
ACLs added without a deactivation date or owner — they accumulate forever and nobody cleans up. Add metadata fields for owner and review-by date and run a quarterly cleanup script. ACLs with conditions that reference nullable fields without null guards — the ACL silently denies when the field is empty, producing false-negative access denials. Test with realistic data including null cases. Field-level read ACLs that hide a field on the form but leave it queryable via REST — fix by adding the same condition to the table-level read ACL.
What Changed in 2026
The Zurich release tightened default behavior on table-level ACLs — operations that previously fell through to row-level evaluation now require an explicit table-level permit. If you upgrade from a pre-Zurich release, expect to find ACL configurations that worked by accident and now need explicit table-level rules. The upgrade tasks dashboard surfaces these.
Implementation Sequence
Audit existing ACLs for wildcards and remove them in priority order (most-used tables first). Add owner and review metadata to every custom ACL. Establish a quarterly review cadence with the security team. Add ACL-impacting changes to the standard change review checklist. Trying to refactor the entire ACL model in one project is a year-long stall; iterate by table.
What to do this week: query sys_security_acl for active rules with name LIKE '*' — that count is your wildcard cleanup queue.