← Home
🔥0 DAY
0 XP
Interview Prep · Platform Security

ACL
SCRIPTING.

ServiceNow Access Control Lists look like a checkbox UI until an interviewer asks why a user with the right role still sees a blank field. Below are four ACL scripting lessons covering the answer variable, gs.hasRole(), table-vs-field evaluation order, and write-time transition guards — each with a runnable simulator trace.

Tap a lesson to inspect the simulator. Pair with the scenario-based scripting guide for full interview coverage.

  1. 1. The 'answer' variable — the only thing ACLs read

    An ACL script runs and returns true, but the user still can't read the record. Why?

    How to answer

    • ACL scripts don't return — they SET a variable named answer.
    • answer = true grants, answer = false denies. A return value is silently ignored.
    • Default answer is false in scripts, so missing assignments deny access.
    • Use gs.getUser() / current.* to compute the decision, then assign answer once at the end.

    Reference script

    // Read ACL on incident — only assignee or admin
    answer = false;
    if (gs.getUserID() == current.assigned_to
        || gs.hasRole('admin')) {
      answer = true;
    }

    Pitfall

    Writing `return true` in an ACL script does nothing — the engine never reads the return value. Always assign `answer`.

  2. 2. gs.hasRole() — single role, role list, and the admin shortcut

    Your ACL must allow itil OR catalog_admin, but you also want admins to bypass. What's the cleanest script?

    How to answer

    • gs.hasRole('admin') is true for any role chain that includes admin — admins inherit everything.
    • Pass a comma-separated string to check multiple roles in one call: gs.hasRole('itil,catalog_admin').
    • Prefer gs.hasRole() over gs.getUser().hasRole() — the former is null-safe in scoped apps.
    • Don't AND 'admin' with another role — admins should pass on their own.

    Reference script

    answer = gs.hasRole('admin')
           || gs.hasRole('itil,catalog_admin');

    Pitfall

    gs.hasRole('itil') AND gs.hasRole('admin') accidentally locks admins out when they lack itil. Use OR, and let admin be its own branch.

  3. 3. table.* vs table.field — the evaluation order

    A user can read incident records but the description field is blank. Which ACL fired?

    How to answer

    • ACLs evaluate from most specific to least: table.field → table.* → parent table.* (sys_metadata, etc.).
    • For a field read, ServiceNow checks the field-level ACL first; if none match, it falls back to table.*.
    • If table.* grants but incident.description denies, the field is hidden — record reads succeed, field is masked.
    • Always test with an impersonation, not as admin — admin shortcuts almost every ACL.

    Reference script

    // incident.description (read ACL)
    answer = gs.hasRole('admin')
           || gs.hasRole('incident_manager');
    
    // incident.* (read ACL) — broader fallback
    answer = gs.hasRole('itil');

    Pitfall

    A field ACL that denies doesn't block the row — it only blanks the field. Junior devs assume the whole record is hidden and waste hours debugging the wrong rule.

  4. 4. Write ACLs — guarding state transitions with current vs previous

    Only allow closing an incident if it was previously 'Resolved'. How would you script the write ACL on the state field?

    How to answer

    • Write ACLs see both current (new value being saved) and previous (value in DB).
    • previous.state holds the value before the form submit — perfect for transition guards.
    • Combine with gs.hasRole() to scope by persona; deny by default at the top of the script.
    • Keep transition matrices in a script include if you have more than a few rules.

    Reference script

    // incident.state write ACL
    answer = false;
    var moving_to_closed = current.state == 7;       // Closed
    var was_resolved    = previous.state == 6;       // Resolved
    if (gs.hasRole('admin')) {
      answer = true;
    } else if (moving_to_closed && was_resolved
               && gs.hasRole('itil')) {
      answer = true;
    }

    Pitfall

    Comparing GlideElement to a number with == works, but === fails — GlideElement is an object. Cast with +current.state when you need strict equality.

Keep going

ACLs interlock with Business Rules and Client Scripts — pair this guide with the glossary and timed drills to lock in the vocabulary.