← All KB Checks
HIGHLinterCheck

GlideRecord vs GlideRecordSecure

nowisor-glide-record-vs-secure··Source on GitHub →

GlideRecord vs GlideRecordSecure

This page describes the nowisor-glide-record-vs-secure check 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 to new 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:

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:

  1. Data the caller is entitled to under their own ACLs — replace new GlideRecord(table) with new GlideRecordSecure(table). This is the majority of findings in customer-facing surfaces (portal widgets, scripted REST APIs serving end users).

  2. Administrative tooling that legitimately bypasses ACLs — leave the GlideRecord call. 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.

  3. 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:

  1. Service Portal widget server scripts (sp_widget records) — highest blast radius.
  2. Scripted REST APIs (sys_ws_operation) called by integrations using non-admin service accounts.
  3. Script Includes with client_callable = true.
  4. Business Rules that run as the user (most run as system; check when and role_conditions).
  5. UI Actions with client = true that call server-side functions.
  6. 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:

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.