← All KB Checks
HIGHScriptOnlyCheck

Cross-Scope Privilege Grants

nowisor-cross-scope-privilege-grants··Source on GitHub →

Cross-Scope Privilege Grants

This page describes the nowisor-cross-scope-privilege-grants check from the nowisor Instance Scan Pack v1.0.0. The check queries sys_scope_privilege for grants from non-global application scopes to the global scope with high-impact operations (write, create, delete) and status = 'allowed'. A single aggregate finding lists sample grants (capped at 20) showing the source scope, target type, target name, and operation; the evidence object includes a total count and overflow indicator.

What this finding means

Your instance has at least one sys_scope_privilege record granting a non-global scoped application the ability to write, create, or delete records in the global scope. Each such grant is a scope-escalation path — code running in the scoped application can perform privileged operations on data and configuration that the scope's documented boundary should not reach.

A few field-level details that matter for triage:

A note on the check's predicate history: the v1.0.0-build version of this check used invalid dot-walk syntax (source.scope and target.scope) that the ServiceNow query engine silently dropped. The result was that the check produced zero findings on a real instance — the predicate didn't filter, the underlying data had matches, but the loop never iterated. Tier 2 verification surfaced the issue, and the predicate was rewritten with the correct flat field names (source_scope and target_scope) plus the status='allowed' filter. The fix is documented in V1_RETROSPECTIVE_TIER2.md. From v1.0.0 GA forward, the check produces correct findings.

Why it matters for your compliance

Cross-scope privilege grants are how scoped applications interact with each other and with the global scope. The architecture exists for legitimate reasons — third-party apps need to integrate, custom apps need to interact with platform data. But the same mechanism is the lever for scope-escalation when grants are over-broad or granted to compromised apps.

NIS2 Article 21§2(a) — risk analysis and information system security policies. NIS2's risk-analysis expectations include the supply-chain and integration surfaces — every cross-scope grant is a documented decision about what one piece of code can do to another. The finding surfaces those decisions so they can be reviewed.

ISO 27001 A.8.3 — information access restriction. A.8.3 expects access restrictions to be aligned with the access control policy. Cross-scope grants are technical implementations of "this application can write to this global-scope target." When grants accumulate without periodic review, the implementation diverges from the documented policy.

The check is severity 2 (high) because the failure mode is conditional: the grant itself is not active exposure, but it pre-positions exposure for any future compromise of the scoped application. On a stable instance with 70-80 high-impact grants (typical for an instance with many third-party apps), each grant is a question worth answering — but the answer is often "yes, this is intentional and approved."

The attack path

The path runs through compromise of a scoped application.

Step 1 — Identify a scoped app with high-impact grants. The attacker enumerates sys_scope_privilege (typically requires admin-level access for the enumeration, but the grant data is also visible to anyone with read access on the table). They identify a candidate: a custom scoped application with write grants to global for several table types.

Step 2 — Compromise the scoped application. Multiple paths exist:

Step 3 — Exercise the grant. The compromised application's code executes a GlideRecord operation against the global-scope target. The platform consults sys_scope_privilege, sees the allowed grant, and permits the operation. The mutation happens.

Step 4 — Operate. The mutation might be: granting an admin role to a target user, modifying an ACL to weaken access controls, deleting an audit record, or inserting a backdoor business rule that fires on future record creation. The action appears in sys_audit as performed by the scoped application — not by an individual user — which can obscure the attribution if the defender isn't watching the application activity specifically.

The defense is to audit the grants regularly. The application's developer team should be able to articulate, for every grant, the business case that justifies it. Grants without articulable cases are candidates for removal.

How to fix it

The remediation is a per-grant review.

Step 1 — Open the grant list. Use the URL pattern from the finding's evidence to filter sys_scope_privilege:

/sys_scope_privilege_list.do?sysparm_query=source_scope.scope!=global^target_scope.scope=global^operationINwrite,create,delete^status=allowed

This shows every high-impact grant the check is auditing.

Step 2 — Categorize each grant. For each row:

  1. Vendor-provided integration grant — the grant ships with a third-party application from the ServiceNow store. Verify the vendor's documentation describes the grant; document the dependency in your application inventory.

  2. Custom app's internal grant — the grant was created by your developers when building a custom scoped application. Confirm the business case: what global-scope operation does the custom app need to perform, and why couldn't it be done with a less privileged mechanism (e.g., a scheduled job running in global scope that the custom app triggers)?

  3. Grant of unknown origin — the grant exists but no one knows why. This is the highest-priority category. Either find the documentation (in change records, deployment notes, or the developer's commit messages), or remove the grant and observe what breaks. A grant that nothing depends on is a grant that should not exist.

  4. Historical grant from a deprecated app — the source application is no longer in use but its grants remain. Remove them. Scoped application uninstallation does not always cascade-clean its privilege grants.

Step 3 — Tighten the grants that remain. For each grant you retain, verify that:

Step 4 — Establish review cadence. Set up a quarterly review (or annually for stable environments) where each grant is re-confirmed. The review record becomes the compliance documentation.

How to verify the fix

/*
 * Verify nowisor-cross-scope-privilege-grants progress
 * Read-only, safe for production.
 * Counts high-impact cross-scope grants by source scope.
 */
(function auditGrants() {
    var bySource = {};
    var gr = new GlideRecord('sys_scope_privilege');
    gr.addQuery('source_scope', '!=', 'global');
    gr.addQuery('target_scope', 'global');
    gr.addQuery('operation', 'IN', 'write,create,delete');
    gr.addQuery('status', 'allowed');
    gr.query();
    while (gr.next()) {
        var src = gr.getDisplayValue('source_scope') || gr.getValue('source_scope');
        bySource[src] = (bySource[src] || 0) + 1;
    }
    var names = Object.keys(bySource).sort();
    gs.print('=== High-impact cross-scope grants by source ===');
    gs.print('Total source scopes: ' + names.length);
    gs.print('');
    var total = 0;
    for (var i = 0; i < names.length; i++) {
        gs.print('  ' + names[i] + ': ' + bySource[names[i]] + ' grant(s)');
        total += bySource[names[i]];
    }
    gs.print('');
    gs.print('Total high-impact grants: ' + total);
})();

Track the count over time. A successful remediation reduces the number of source scopes (apps that hold any grants) and the per-app grant count. The expected end state is "every remaining grant has a documented owner and an approved business case."

What to do next

Cross-scope grants interact with the broader privilege-cluster findings:

For organizations with significant scoped-app deployments (custom apps, third-party integrations), the nowisor advisor product is being built to group cross-scope findings by the source application — surfacing which apps hold the highest aggregate privilege weight, so app inventory and security review align with the actual exposure surface. Audit your scope privilege surface →