Every now and then, a well-meaning developer points to a Query Business Rule and says: “We’ve locked it down, we’re good.” On the surface, it looks true. The list only shows the user their own records. The data seems restricted. Job done, right?
Not quite. What that developer has done is like putting up a “No Entry” sign on a road and assuming no one will walk through the field right next to it.
In this article, I’ll break down what ACLs and Query Business Rules each do, why both are essential, and most importantly, walk through every known way a Query Business Rule can be bypassed, leaving your data completely exposed if ACLs aren’t there to back it up.
What Are We Actually Talking About?
Before diving into the good stuff, let’s define our two players.
ACL (Access Control List) is a platform-level security mechanism in ServiceNow. It controls who can Read, Write, Create, or Delete records and fields, based on roles, conditions, or scripts. ACLs are enforced by the platform itself and they are always on, regardless of how data is accessed.
Query Business Rules are Business Rules that fire on the “Before Query” timing. They run before the database query executes, injecting extra filter conditions to restrict what rows get returned. Think of them as a smart filter that says: “Whenever anyone queries this table, automatically add WHERE assigned_to = current_user.”
The Bank Vault Analogy
Here is a simple way to remember the difference:
- ๐ฅ๏ธ Query Business Rule = The bank teller who only shows you your own account on screen. Fast, efficient, and great for the user experience and it filters data before you even see it.
- ๐ ACL = The vault door with a biometric lock. Even if someone hacks the teller’s screen, they physically cannot open the vault. No exceptions.
A clever customer cannot bypass the vault door. But they absolutely can find a way around the teller’s screen. That is the relationship between ACLs and Query Business Rules.
Query Business Rules: The Performance Layer
Query Business Rules are one of the most effective tools for keeping ServiceNow fast. Because they inject filter conditions before the SQL query hits the database, the database engine does the heavy lifting and not your script.
Without a Query BR, a GlideRecord query on a large table fetches potentially thousands (or millions) of rows, and only then applies your conditions in JavaScript. With a Query BR, the database returns only the records that actually matter.
Here is a simple example. The following Before Query Business Rule ensures a technician only sees incidents assigned to them:
(function executeRule(current, previous) {
// Only show admins the full list; everyone else sees only their own
if (!gs.hasRole('itil_admin')) {
current.addQuery('assigned_to', gs.getUserID());
}
})(current, previous);
Consider a table with 2 million incident records. Without this rule, every list load fetches all 2 million rows and filters them in memory. With this Query BR in place, the database returns only the ~200 records assigned to that user. The difference in response time and server load is enormous, especially in high-traffic environments.
Performance gains from Query BRs include: faster list loads, lower server memory usage, reduced database CPU, and better overall scalability.
The Problem: Query Business Rules Can Be Bypassed
Here is where things get uncomfortable. Query Business Rules are application-layer filters. They run as part of the normal ServiceNow scripting engine, and that scripting engine can be told to stand down.
There are multiple legitimate sounding reasons why developers bypass Business Rules: performance, bulk data operations, cross-scope integrations. But each of these bypass methods silently skips your Query BRs, leaving your data completely unrestricted.
Let’s go through every one of them.
1. gr.setWorkflow(false) in GlideRecord
This is the most common bypass. When a developer calls setWorkflow(false) on a GlideRecord object before running a query, ServiceNow skips all Business Rules, including your Before Query rules.
var gr = new GlideRecord('sensitive_data_table');
gr.setWorkflow(false); // ALL Business Rules are now skipped โ including Query BRs
gr.query();
while (gr.next()) {
gs.info(gr.getValue('confidential_field')); // Returns ALL records, no restrictions
}
This pattern is used routinely in background scripts, scheduled jobs, and bulk update operations. Any developer with Script access can do this. Without an ACL, every record in that table is now readable.
The proof: ServiceNow’s own platform documentation states: “GlideRecord.setWorkflow(false) will allow the query to bypass Before Query Business Rules โ but NOT ACLs or security constraints.” This is the exact reason ACLs are irreplaceable.
2. Transform Maps โ “Run Business Rules” Unchecked
In every Transform Map configuration, there is a checkbox called “Run business rules”. When this is unchecked, which is commonly done for performance during large data imports and all Business Rules on the target table are skipped. This includes your Query BRs.
An integration developer migrating data, or anyone with access to configure Transform Maps, can import and process records without any of your data restriction rules ever firing. They get to see and manipulate whatever is in the staging table, completely unrestricted.
3. Background Scripts & Fix Scripts
Background Scripts and Fix Scripts run in an elevated, admin level context. They are commonly used and recommended with setWorkflow(false) to improve bulk operation performance. This means any admin level user can run a script that:
- Bypasses all Before Query Business Rules
- Queries any table with zero restrictions from your Query BRs
- Reads, updates, or exports sensitive data silently
// Typical Background Script / Fix Script pattern
var gr = new GlideRecord('hr_case');
gr.autoSysFields(false);
gr.setWorkflow(false); // Standard performance trick โ also kills your Query BRs
gr.query();
// All HR cases returned โ regardless of what your Query BR says
4. Scheduled Jobs
Scheduled Jobs run as system level processes, often under admin or service account roles. Developers frequently add setWorkflow(false) to scheduled job scripts as a performance best practice for bulk operations. This is perfectly accepted behavior, but it means every time that job runs, your Query BR is completely invisible.
If someone with the right access modifies a scheduled job, they now have a scheduled, automated, recurring mechanism to extract unrestricted data with no Query BR ever firing.
5. Scripted REST APIs
When building Scripted REST API endpoints, the developer controls the GlideRecord queries directly. A developer can write a REST endpoint that calls setWorkflow(false) internally, bypassing Query BRs entirely. The caller of the API gets back data with no row level filtering applied.
(function process(request, response) {
var gr = new GlideRecord('restricted_table');
gr.setWorkflow(false); // Query BR silently skipped
gr.query();
// Returns ALL records to the API caller โ no restriction applied
})(request, response);
Why ACLs Are the Real Line of Defence
Notice something across all seven bypass methods above? Every single one of them skips Business Rules. But here is what ServiceNow’s own platform documentation confirms:
“setWorkflow(false) bypasses all Business Rules โ but NOT ACLs or security constraints.”
ACLs are evaluated at the platform kernel level, not the application script level. They apply regardless of how a record is accessed. Here is what that looks like in practice:
- โ List views and form views โ ACL enforced
- โ
GlideRecord queries, even with
setWorkflow(false)โ ACL enforced - โ Transform Maps with “Run Business Rules” unchecked โ ACL enforced
- โ REST Table API calls โ ACL enforced
- โ Reports and scheduled exports โ ACL enforced
- โ Flow Designer and IntegrationHub actions โ ACL enforced
- โ Background Scripts and Fix Scripts โ ACL enforced
There is no setWorkflow(false) equivalent for ACLs. They just work. This means an ACL that says “only HR Managers can read HR Case records” will hold true no matter what path the data takes โ no exceptions.
Side by Side: What Each One Actually Does
| Scenario | Query BR Helps? | ACL Required? |
|---|---|---|
| Speed up large table list loads | โ Yes โ reduces DB rows fetched | โ Not a performance tool |
Prevent setWorkflow(false) bypass | โ Bypassed completely | โ ACL enforces anyway |
| Restrict records in list view | โ Filters the UI query | โ Security backstop |
| Protect sensitive fields (field-level) | โ Not field-level aware | โ Field-level ACL supported |
| Block Transform Map with BRs unchecked | โ Not executed | โ Still enforced |
| Protect against Background Script abuse | โ Bypassed | โ ACL still applies |
| Reduce server memory and CPU usage | โ DB-level filtering | โ No performance benefit |
The Rule Is Simple
- Use Query Business Rules to make your instance fast.
- Use ACLs to make your instance secure.
Query BRs without ACLs are a security illusion, one setWorkflow(false) away from collapse. ACLs without Query BRs are a performance problem and every query hits the full table before restrictions apply. Both together? That’s the right architecture !!
Closing Thoughts
ACLs and Query Business Rules are not competing tools, they are teammates with different jobs. One makes your system fast by filtering at the database layer. The other makes it secure by enforcing access at the platform level, unconditionally.
The next time someone tells you a “Query Business Rule is enough to secure a table“, you now have atleast 5 concrete reasons “to explain exactly why it isn’t“
Build both. Always.

Leave a Reply