Cross-Domain Script Include Reference
This page describes the
nowisor-domain-separation-script-includecheck from the nowisor Instance Scan Pack v1.0.0. The check is a LinterCheck that targets only Script Includes (not Business Rules or UI Actions). It applies a heuristic two-pass AST visitor: flag any Script Include that mutates auth or session state (setRolesorsetSessionis called) and does not referencesys_overridesanywhere in the script body. The finding is per-script (one finding per matching Script Include). Expected volume depends on whether the instance uses domain separation — non-DS instances will produce findings that are advisory rather than defects.
What this finding means
Every finding identifies one Script Include whose body contains a call to setRoles or setSession (the auth/session mutation primitives) but does not contain a reference to sys_overrides (the platform's domain-aware override table). The combination is a heuristic signal that the script was written for a non-domain-separated mental model.
The reasoning: on a domain-separated instance, scripts that mutate authentication or session state must participate in the domain-override model — when a role list is being computed, the script should consult sys_overrides to apply per-domain customizations rather than defaulting to a global role set. A script that calls setRoles without ever touching sys_overrides is either:
- Correct on a non-DS instance (the script doesn't need domain-aware behavior because there are no domains)
- Incorrect on a DS instance (the script will apply globally-defined role behavior in a tenant context, potentially leaking privileges across domain boundaries)
The check cannot tell which case applies — that's an instance-level configuration question. The check is therefore explicitly heuristic. Findings on a non-DS instance are advisory: they confirm the script will work as-is today but flag it as a code smell to address before any future DS migration. Findings on a DS instance are higher-priority: they may represent real cross-domain leakage paths that need investigation.
A technical note on the predicate: the second pass uses LITERAL.getValue() to find the string 'sys_overrides' anywhere in the script. This API has uncertainty in some platform releases — if the literal-value extraction fails, the check defaults to "no sys_overrides found," which over-reports rather than under-reports. The intent is the safer default for a heuristic check: a false positive is a code-review prompt, a false negative is a missed leakage path.
Why it matters for your compliance
Domain separation is the platform's mechanism for safe multi-tenancy. Regulators that examine multi-tenant environments — particularly MSPs, regulated enterprises operating regional subsidiaries, and entities with strict data residency requirements — treat domain separation as a load-bearing control.
NIS2 Article 21§2(j) — identity and access management. Identity management in a domain-separated context must respect tenant boundaries. A script that mutates roles without domain awareness violates the tenant isolation that the IAM architecture relies on. NIS2 expects entities operating in regulated sectors to maintain this isolation; cross-domain privilege leakage is the canonical failure mode the article addresses for multi-tenant deployments.
ISO 27001 A.5.31 — legal, statutory, regulatory and contractual requirements. A.5.31 specifically addresses the regulatory commitments an entity makes about how it handles others' data. In MSP and shared-tenant deployments, those commitments often include explicit tenant-isolation language — "we will not allow data or privileges from one customer's domain to affect another customer's environment." A script that violates that promise via cross-domain role mutation is a direct A.5.31 finding.
GDPR Article 32 — security of processing. Multi-tenant environments under GDPR are required to maintain technical isolation between data controllers. Cross-domain privilege leakage is a Article 32 finding because it represents a technical failure of the isolation guarantee. This check is one of two in the pack that maps to GDPR Article 32 directly, reflecting how heavily multi-tenancy weighs in GDPR-compliant deployments.
Severity 2 (high) — the failure mode is conditional on the instance actually using domain separation. On a non-DS instance, the finding is advisory. On a DS instance, the finding requires investigation. The check's severity reflects this conditionality.
The attack path
The attack path is specific to domain-separated deployments. On non-DS instances, the path does not apply (advisory finding only).
Step 1 — The MSP context. Your instance is configured with multiple domains. Each domain corresponds to a tenant — a customer of your managed-services business, or a regional subsidiary, or a regulated business unit. Each tenant's users have access only to their own domain's data; that isolation is the value proposition of the multi-tenant deployment.
Step 2 — The non-DS-aware script. A Script Include exists that provides "temporary elevation" for support purposes:
var SupportElevation = Class.create();
SupportElevation.prototype = {
elevate: function() {
var user = gs.getUser();
user.setRoles('admin');
// ... operation that requires admin
},
type: 'SupportElevation'
};
The script does not reference sys_overrides. It applies the global admin role unconditionally. On a non-DS instance this works as intended. On a DS instance, the global admin role grants visibility into every domain's data.
Step 3 — The cross-tenant exposure. A support engineer working on Tenant A's domain invokes the elevation script for a debugging session. The script calls setRoles('admin') on their session. For the remainder of the request, their session has admin privileges — which, in a DS context, means they can read records in every domain, including Tenant B's data.
Step 4 — The leakage path. If the engineer's next action queries records (legitimately scoped to Tenant A in their mental model), the platform returns records from every domain. If the script's subsequent code writes to a globally-scoped table or returns data to the engineer's UI, Tenant B's data leaks out. The leakage may be intentional (the engineer reads it), accidental (the data appears in a debug log), or invisible (it flows into a downstream report that the engineer doesn't review).
The defense is structural: the elevation script should consult sys_overrides to determine the appropriate elevated role list for the current domain — a list that grants visibility only within that domain, not across the deployment.
How to fix it
The check is heuristic. The fix path depends on the instance's actual configuration.
Step 1 — Determine if your instance uses domain separation. Run this check in Background Scripts:
if (!GlideTableDescriptor.isValid('domain')) {
gs.print('domain table does not exist on this instance.');
gs.print('Domain separation plugin is not active. Findings are advisory.');
} else {
var domains = new GlideRecord('domain');
domains.addQuery('active', true);
domains.query();
gs.print('Active domains: ' + domains.getRowCount());
// If > 1, DS is in use; if = 1 (just global), DS is not in effective use.
}
If your instance does not use domain separation in any meaningful sense — only the global domain has activity — the findings are advisory. They flag patterns that would need attention before any future DS migration but are not active defects.
Step 2a — If DS is not in use: Document the finding cluster as "monitored advisory" in your code review record. For each Script Include flagged, add an inline comment noting that the script does not currently need DS awareness because the instance is not DS-active. When a DS migration is planned, revisit the findings.
Step 2b — If DS is in use: Each finding needs individual review.
- Audit the script's actual domain reachability. Does the script run only in the
globaldomain, or can it run in tenant domains? Scripts that execute in tenant context need DS awareness. - Audit the role list. What role does the script call
setRoleswith?adminandsecurity_adminhave cross-domain visibility; tenant-scoped roles do not. - Add
sys_overridesparticipation. Where appropriate, refactor the script to querysys_overridesfor the current domain's elevated-role configuration — applying a domain-specific role list rather than a global one. The pattern looks roughly like:
var override = new GlideRecord('sys_overrides');
override.addQuery('record', current.sys_id);
override.addQuery('domain', gs.getUser().getDomainID());
override.query();
if (override.next()) {
// Apply domain-specific role list
} else {
// Fall back to global default
}
- Prefer
GlideImpersonateoversetRoles()for impersonation use cases. The impersonation API has domain-aware variants in newer platform releases and provides audit logging thatsetRoles()does not.
Step 3 — Document and re-scan. Each remediated script should have inline documentation describing the DS-awareness intent. Re-run the scan; the finding should clear for scripts that now reference sys_overrides.
How to verify the fix
Pull the current findings:
/*
* Verify nowisor-domain-separation-script-include remediation progress
* Read-only, safe for production.
* Lists Script Includes flagged for missing sys_overrides reference.
*/
(function pullDomainSeparationFindings() {
var f = new GlideRecord('scan_finding');
f.addQuery('check.sys_scope.scope', 'x_nowisor_isp');
f.addQuery('check.name', 'Cross-Domain Script Include Reference');
f.query();
gs.print('=== Domain-separation findings ===');
gs.print('Total Script Includes flagged: ' + f.getRowCount());
gs.print('');
while (f.next()) {
gs.print(' ' + f.getDisplayValue('source'));
}
// Instance DS context (informational)
gs.print('');
if (!GlideTableDescriptor.isValid('domain')) {
gs.print('Instance domain count: 0 (domain table absent, DS plugin not active)');
gs.print('Findings are ADVISORY (DS not in active use)');
} else {
var domains = new GlideRecord('domain');
domains.addQuery('active', true);
domains.query();
gs.print('Instance domain count: ' + domains.getRowCount());
if (domains.getRowCount() <= 1) {
gs.print('Findings are ADVISORY (DS not in active use)');
} else {
gs.print('Findings are ACTIONABLE (DS in active use)');
}
}
})();
For a specific remediated Script Include, the verification is: open the script body, confirm a reference to sys_overrides exists. The reference can be a GlideRecord('sys_overrides') call, a comment naming the table, or a string literal — the predicate is lenient because the heuristic is over-reporting by design.
What to do next
Domain-separation findings tie into the broader access-control and code-discipline clusters:
nowisor-set-roles-detector— finds everysetRoles()call in the platform, not just those in Script Includes. Combine with this check to triage the full role-mutation surface.nowisor-cross-scope-privilege-grants— privilege grants across application scopes. On DS-active instances, scope and domain interact; both audits should run together.nowisor-eval-usage-detectorandnowisor-glide-evaluator-detector— dynamic-evaluation findings that, in a DS context, can produce cross-domain code execution if the evaluated string is influenced from a different domain.nowisor-set-workflow-false-detector— audit-bypass in DS contexts is especially serious because cross-domain operations are exactly the operations the audit trail needs to capture.
For MSPs and regulated multi-tenant deployments under GDPR Article 32, the nowisor advisor product is being built to produce a "tenant-isolation evidence pack" that combines the domain-separation findings with the ACL audit, the cross-scope privilege audit, and the configuration-baseline drift across domains — giving compliance teams a single artifact to demonstrate tenant-isolation control effectiveness. See the multi-tenant isolation evidence flow →