GlideRecord vs GlideRecordSecure
This page describes the
nowisor-glide-record-vs-securecheck from the nowisor Instance Scan Pack v1.0.0. The check is a LinterCheck — it walks every server-side script (Script Includes, Business Rules, UI Actions) and emits one finding per call tonew GlideRecord(). A typical fresh instance produces several hundred findings; this is expected and not, on its own, evidence of compromise.
What this finding means
Every finding identifies one line of server-side code that constructs a GlideRecord rather than a GlideRecordSecure. The two APIs share a near-identical surface, but their security semantics are fundamentally different:
new GlideRecord('table')— queries the table with no ACL evaluation. Every record matching the encoded query returns, including records the calling user would otherwise be denied. This is the default the platform has shipped for years; most developer documentation uses it.new GlideRecordSecure('table')— queries the same table with ACL evaluation enabled. Row-level ACLs, field-level read ACLs, and table-level read ACLs all apply. Records the user cannot read are silently excluded from results; fields the user cannot read return empty strings.
The check produces no finding when no GlideRecord calls exist (rare). It produces one finding per call site. Each finding includes the source table, the script record, and the line number, all parseable from the v1 finding schema metadata block.
This check is the loudest LinterCheck in the pack on purpose. The platform's secure-by-default story is real for many surfaces, but not this one — every script author chooses between the two APIs explicitly, and the choice carries security weight that the default behavior does not surface.
Why it matters for your compliance
This is a code-discipline finding, and modern regulators are increasingly explicit about secure-coding evidence.
NIS2 Article 21§2(h) — policies regarding cryptography and access control. NIS2's drafters explicitly include access-control discipline at the code level under this article. Documented secure-coding standards that mandate GlideRecordSecure (or document the explicit exception for administrative tooling) is the kind of evidence supervisors expect.
ISO 27001 A.8.3 — information access restriction. The control text requires that information access be restricted in accordance with the access control policy. When a Service Portal widget queries an HR table via GlideRecord, the platform's ACL policy is bypassed in the application layer — the access restriction exists in the ACL, but the code goes around it. Auditors increasingly look for code-level evidence that the ACL policy is honored by application code, not just present in the configuration.
The findings are inventory, not defects. The compliance value comes from the triage process — for every finding, you either remediate it (switch to GlideRecordSecure), or you document the explicit decision to leave it as GlideRecord. The documented decisions become the evidence package.
The attack path
The canonical attack path runs through Service Portal widgets, which execute server scripts with SYSTEM privilege regardless of the portal user's role.
Step 1 — The widget. A Service Portal widget for "My HR Cases" queries the sn_hr_core_case table with GlideRecord. The developer added a filter opened_for = $sp.getUser().sys_id and tested with their own login. Tests pass.
Step 2 — The interception. An authenticated portal user (any user — the attack works with any role that can load the portal page) opens the page in the browser. The browser issues an XHR to load the widget. The widget's server script runs.
Step 3 — The bypass. The attacker intercepts the XHR via browser dev tools or a proxy. The filter parameter opened_for is supplied from the client side — the attacker rewrites it to another user's sys_id, or removes it entirely.
Step 4 — The exfiltration. Because the server script uses GlideRecord, the query runs as SYSTEM. The modified filter returns HR cases for any employee — including salary disputes, disciplinary actions, medical leave requests. The widget happily serializes the result back to the client.
The same pattern attacks CMDB tables (cmdb_ci_server → full asset inventory), email logs (sys_email → credential reset chains), and incident records with PII in descriptions. The widget surface is the most common entry point; the underlying issue is the API choice in any server-side script invocable from a less-privileged context.
For non-widget contexts, the same risk applies to Scripted REST APIs called by integrations, scheduled jobs that build report data for less-privileged downstream consumers, and any Script Include called from a client-callable interface.
How to fix it
This is a triage check, not a one-shot remediation. The findings are inventory; the value is in working through them with intent.
For each finding, classify the call site:
Data the caller is entitled to under their own ACLs — replace
new GlideRecord(table)withnew GlideRecordSecure(table). This is the majority of findings in customer-facing surfaces (portal widgets, scripted REST APIs serving end users).Administrative tooling that legitimately bypasses ACLs — leave the
GlideRecordcall. Add an inline comment documenting why the bypass is intentional, and ensure that the caller has been authenticated as an administrator upstream of this script.Reports or scheduled jobs that aggregate data across ACL boundaries — usually correct to keep
GlideRecord(the script's purpose is to aggregate beyond what any one user can see), but the output of that aggregation must be sent only to users authorized to see aggregate data.
Document the classification of every finding in your code review record. The documentation is the compliance artifact; the per-call decisions are the defensible position.
Order of triage:
- Service Portal widget server scripts (
sp_widgetrecords) — highest blast radius. - Scripted REST APIs (
sys_ws_operation) called by integrations using non-admin service accounts. - Script Includes with
client_callable = true. - Business Rules that run as the user (most run as system; check
whenandrole_conditions). - UI Actions with
client = truethat call server-side functions. - Scheduled jobs and report scripts.
How to verify the fix
Findings are emitted per call site, so verification is per-finding. Use this Background Script to pull the current open findings for this check, grouped by script source:
/*
* Verify nowisor-glide-record-vs-secure remediation progress
* Read-only, safe for production.
* Lists open findings grouped by source script.
*/
(function pullFindings() {
var byScript = {};
var f = new GlideRecord('scan_finding');
f.addQuery('check.sys_scope.scope', 'x_nowisor_isp');
f.addQuery('check.name', 'GlideRecord vs GlideRecordSecure');
f.query();
while (f.next()) {
var sourceName = f.getDisplayValue('source') || '(unknown)';
if (!byScript[sourceName]) byScript[sourceName] = 0;
byScript[sourceName]++;
}
var names = Object.keys(byScript).sort();
gs.print('=== GlideRecord findings by source ===');
gs.print('Total scripts with findings: ' + names.length);
gs.print('');
for (var i = 0; i < names.length; i++) {
gs.print(' ' + names[i] + ': ' + byScript[names[i]] + ' call(s)');
}
})();
Track the report over time. A successful remediation campaign shows the count dropping monotonically as scripts move to GlideRecordSecure. The expected end state is non-zero — administrative tooling and aggregation jobs will (correctly) keep some GlideRecord calls — but every remaining call should be documented as intentional.
To verify a specific remediated script, open the corresponding scan finding record (the source field) and confirm the GlideRecord line has been replaced with GlideRecordSecure in the script body.
What to do next
The code-analysis cluster of checks looks at related code-discipline failure modes that often co-occur:
nowisor-set-roles-detector— flags calls tosetRoles()that mutate session role state. These calls are rare and warrant individual review.nowisor-eval-usage-detector— flagseval()calls. Combined withGlideRecord,eval()enables arbitrary-data exfil from arbitrary scripts.nowisor-set-workflow-false-detector— flags calls that bypass Business Rules and audit. AGlideRecordquery that fetches data, plus asetWorkflow(false)on the write path, eliminates both ACL evaluation and audit trail for the round trip.nowisor-glide-evaluator-detector—GlideEvaluator.evaluateString()iseval()on a different name. Same risk class.
The nowisor advisor product is being built to group these findings into the code-discipline cluster with prioritized remediation guidance based on caller reachability and table sensitivity, so your engineering team can work the top of the list first. Learn more at nowisor.com.