Home / Question Hub / Platform & Security

Platform & Security

ACLs, UI and Data Policies, Update Sets and Notifications — the configuration layer where security and governance questions live.

No questions match your search.

ACL (Access Control)

19 questions

An Access Control rule (sys_security_acl) that decides whether a user may perform an operation (read, write, create, delete…) on a table or field. Each ACL can require roles, a condition, and a script — all three that are present must pass for that ACL to grant access.

By target: record ACLs (table-level: incident, field-level: incident.state) and other types like UI page, processor, REST endpoint, client-callable script include ACLs. By operation: read, write, create, delete, plus specialized ones (report_on, personalize_choices, edit_task_relations…).

incident.none is the table-level rule — gate to the record itself. incident.* matches every field that lacks its own explicit ACL — the field wildcard/fallback. incident.state targets one field. Evaluation is most-specific-first: field ACL, else wildcard, else parent table's ACLs.

The field stays read-only. ACLs are server-side security; UI policies are client-side presentation. The UI policy may visually unlock the field, but the write ACL rejects the value at save. Security always wins over presentation — the sentence interviewers wait for.

Work the checklist: (1) which table, (2) which operations, (3) record-level or field-level, (4) express the rule as role + condition + script, (5) remember multiple ACLs on the same target are ORed, so model exceptions as additional granting ACLs, not "deny" rules. Then test with impersonation and the Security Debugger.

All of them, within one ACL — role AND condition AND script must each pass. Across multiple ACLs on the same target it flips: passing any one ACL grants access (OR). AND inside, OR between — memorize that asymmetry.

Not with admin alone — modifying ACLs requires the elevated security_admin privilege, activated via "Elevate role" for the session. It's a deliberate speed bump so security rules can't be altered casually.

A checkbox on the ACL: when true (default), users with admin bypass the rule entirely. Unchecking it makes the ACL apply to admins too — used on sensitive data (HR, security incidents) where even admins shouldn't peek.

Honest, structured answer: the model itself is simple (AND within, OR between, specific-before-wildcard); what bites people is the interaction — inheritance from parent tables, wildcard fallbacks, and query BRs muddying "why can't I see this row." Say you tame it with the Security Debugger, impersonation testing, and keeping ACL scripts one line by delegating to Script Includes.

Credible examples: a field ACL that worked on the form but not the list (missed the list mechanics), reports leaking data because report_on wasn't controlled, the "rows removed by security constraints" UX complaint that led to adding a query BR, and a wildcard ACL unexpectedly overriding a new field's intended access until an explicit field ACL was added.

The Security Debugger (Debug Security Rules) is genuinely good: it annotates each form/list element with which ACL evaluated and why it passed/failed, per session. Limitations worth naming: output is verbose on big lists, and it doesn't cover every access path (background scripts, some APIs) — for those you log from the ACL script itself.

Good examples: a client_callable_script_include ACL restricting who may invoke a GlideAjax endpoint; a REST endpoint ACL for a scripted API; report_on to stop a table appearing in reporting; personalize_choices to lock down who edits choice lists; a UI page ACL protecting an admin utility page.

With High Security enabled (every modern instance), the default deny rule applies — no matching ACL means no access for non-admins. A brand-new custom table gets auto-created ACLs granting access to its auto-created role. The trick answer "everyone can see everything" is wrong on current instances.

// Read ACL script on incident
answer = gs.getUser().isMemberOf(current.getValue('assignment_group'));

Pair it with a query Business Rule with the same logic so lists don't show the "rows removed by security" message. ACL = enforcement, query BR = UX.

One field-level read ACL (e.g. incident.u_secret) does all of it — form, list, portal, API — because it's server-side. UI policies would only cover the form. If the requirement is cosmetic (visible nowhere but data not sensitive), you could combine UI policy + list layout + widget changes, but the single ACL is the right answer for actual hiding.

Choices can't be ACL'd individually. Options: an onCellEdit/onChange client script rejecting the transition, a before Business Rule aborting disallowed state changes (the real enforcement), or role-conditioned choice filtering via a client script using g_form.removeOption() on forms. State-flow/model definitions can also restrict transitions declaratively — mention them for change/incident tables.

// before query BR on incident
(function executeRule(current, previous) {
    var grMe = new GlideRecord('sys_user');
    if (grMe.get(gs.getUserID())) {
        current.addQuery('location.country', grMe.getValue('country'));
    }
})(current, previous);

A query BR keyed on a user attribute — the standard data-segregation pattern (domain separation being the heavyweight alternative for true multi-tenancy).

The user can write. ACLs on the same target are ORed — one passing ACL is enough. There is no "deny" ACL that vetoes others; restriction is achieved by ensuring no ACL evaluates true for that user.

Most specific target first: field ACL (incident.state) → field wildcard (incident.*) → table (incident.none) → up the hierarchy (task). For record access the user must pass both a table-level and the applicable field-level rule. Within one ACL: role, then condition, then script (cheapest checks first).

Use both, for different jobs: the query BR filters rows before retrieval (clean lists, correct counts, no security message) but is not a complete security boundary; the read ACL is authoritative on every path but degrades list UX when it does the filtering. Query BR for experience, ACL for enforcement — the pairing is the senior answer.

UI Policy & Data Policy

14 questions

Declarative form rules: when a condition is true, apply actions — make fields visible/hidden, mandatory/optional, read-only/editable — without code. They run client-side after client scripts, in ascending Order, and can optionally run scripts (Execute if true/false) for edge cases.

UI Policy: client-side, forms only, per view, supports visible/read-only/mandatory. Data Policy: server-side, enforces mandatory/read-only on every write path — imports, web services, scripts — and can optionally push to the UI ("Use as UI Policy on client"). If data integrity is the requirement, it must be a Data Policy; UI-only rules are bypassable.

  • Reverse if false — when the condition stops matching, actions undo automatically (hidden becomes visible again).
  • On load — evaluate at form load, not just on subsequent changes.
  • Inherit — the policy also applies to tables extending this one.
  • Global — applies on all views; uncheck to target a specific view.

Actions first, then the script (Execute if true / Execute if false). That's deliberate: the script can build on the state the actions just applied — e.g. actions make a field visible, the script populates it.

Editable — UI policies run after client scripts, so the policy's action lands last and wins. (Flip the artifacts and the client script still loses.) The general law: for the same client-side event, order of application decides, and UI policies apply last.

Template: "When category = Hardware, make CI mandatory and show the asset fields." UI policy because it's pure conditional presentation — no code to maintain, order-controlled, reversible. Not a client script (would be code for what a condition expresses), not an ACL (this isn't security — any user may edit the field; it's about guiding input). Showing you can articulate the selection criteria is the actual answer.

Client-script-only: anything needing server data (GlideAjax), computed values, onSubmit abort, onCellEdit, or g_scratchpad reads. UI-policy-only: nothing strictly — a client script can replicate any UI policy — but UI policy + "Use as UI Policy on client" via Data Policy reaches server-side enforcement, which no client script can. The honest framing: UI policy is a declarative subset; prefer it whenever it suffices.

A hidden mandatory field blocks submission invisibly. The UI policy that hides it must also set it non-mandatory (and Reverse if false restores both when shown again). If the mandatory came from the dictionary, override it with UI policy/data policy logic instead — dictionary-mandatory cannot be relaxed client-side.

Yes — on the UI policy (and catalog UI policy) there are "Run scripts in UI type" / applies-on settings: Desktop, Mobile/Service Portal, or All. A policy whose script uses DOM tricks should be desktop-only; portable policies target All. For catalog UI policies there are separate checkboxes for where they apply (item view, RITM, task).

On the Catalog UI Policy record, check the three "Applies to" boxes: Applies on Catalog Item view, Applies on Requested Items, Applies on Catalog Tasks. Same policy, three contexts — provided the variables it references are visible in those contexts too.

The behavioral curveball. Good shape: answer patiently the first time, then make the answer reusable — write it into the team wiki/knowledge base, point them to it next time, pair on one example, and set up office hours so questions batch instead of interrupting. Shows mentoring instinct and systems thinking, which is what's being probed.

Ask one question: must the rule hold when data arrives without the form? Imports, REST, scripts, mobile — if yes, Data Policy. If it's purely about guiding a human on the form (show/hide especially — data policies can't hide), UI Policy. Many requirements need a Data Policy for mandatory/read-only plus a UI policy for visibility.

The policy must only use what data policies support: mandatory and read-only actions (no visibility), conditions expressible server-side (no client-state dependencies like "user just typed"), and no client scripts attached. In practice you recreate it as a data policy, tick "Use as UI policy on client" to keep the form behavior, then retire the UI policy.

No — visibility is purely presentational, so it's UI policy territory. Data policies control mandatory and read-only only, because those are enforceable on a write operation; "hidden" has no meaning for an inbound REST payload.

Update Sets

17 questions

Capture configuration in an update set in dev → mark Complete → retrieve on test via the remote instance connection → preview (resolve collisions) → commit → validate → repeat onto prod. Scoped apps ride the app repository instead (publish → install). Data (users, records) doesn't travel in update sets — that's import sets or XML.

  • Retrieve via update source (remote instance record) — the standard, auditable path.
  • Export to XML / import XML — for instances that can't talk to each other.
  • For scoped apps: publish to the app repository and install on targets.
  • CI/CD pipelines (CICD API) for automated promotion in mature shops.

Retrieval through a configured update source: the target pulls directly, checksums are verified, preview runs against live target state, and the chain of custody is recorded. XML export is the fallback when connectivity or security policy rules out direct retrieval.

(1) Collisions — the target's version of a record is newer than what the set carries; someone changed it directly on target. Each must be reviewed: accept remote or skip. (2) Missing references — the set references records the target doesn't have (a field, a role, another update set's artifact), usually meaning a dependency set wasn't committed first or batching order is wrong.

Look at the table's dictionary: update-set tracking applies to tables whose records participate in the update mechanism (attribute update_synch=true on the collection). Quick empirical test: change a record and see whether a sys_update_xml entry appears in your current set.

Data records (users, groups, CIs, schedules' entries…) move via XML export/import for small sets, import sets + transform maps for volume, or a fix script that creates them idempotently as part of the release. Never hand-retype on prod — unrepeatable and error-prone.

Every instance has a Default set per scope that silently captures changes made when no set is selected. It should stay empty in disciplined teams — anything landing there is a process slip, and it can't be moved as-is (you'd relocate its entries into a real set). The platform recreates/ensures it exists; you don't complete it.

The platform simply creates a new Default set — there must always be one to catch untracked changes. Completing it doesn't make its contents promotable in a sane way; the correct remediation is moving its entries (sys_update_xml records) into a proper named set.

Merging combines several sets into one, keeping only the newest version of each record. Advantage: one artifact to preview/commit, no ordering mistakes. Disadvantages: it's irreversible, loses per-set granularity (can't back out one feature), and if the merged sets contained conflicting intermediate states you keep only the last. Batching largely supersedes merging.

A parent set groups child sets into a hierarchy that previews and commits as one unit, in the right order, while keeping each child intact. Advantages: preserves granularity, handles cross-set dependencies, one-click promotion of a release. Disadvantage: the whole batch succeeds or is remediated together, and building the hierarchy needs discipline. It's the modern recommended pattern for multi-set releases.

  • Naming convention with story/ticket reference.
  • One logical change per set; batch for releases.
  • Never work in Default; check your current set before touching anything.
  • Complete sets before retrieval; never edit a completed set.
  • Preview seriously — resolve every problem knowingly, don't blanket-accept.
  • Keep data migrations out of update sets (fix scripts / import sets).
  • Back out plan: know what the set touches before committing to prod.

They're data, not configuration — update sets won't capture them. Use XML export/import for one-offs, import sets with transform maps for volume (with coalesce so reruns update instead of duplicate), or re-point both instances at the same authoritative source (LDAP/SSO provisioning) so they converge naturally.

Configuration records: business rules, client scripts, script includes, UI policies/actions, ACLs, form/list layouts, dictionary changes, workflows/flows (published versions), notifications, reports (if chosen), properties changed via the sys_properties form, etc. — anything on update-synch tables.

Data: task records, users/groups/roles assignments, CIs, attachments, schedules' runtime data, scheduled job next-run state, homepages (unless explicitly added), and in-flight workflow contexts. Also changes made on non-update-synch tables and anything done while in a different scope than the set.

Open the source set's Customer Updates (sys_update_xml) related list, select the record(s), and change their Update set reference to the target set (both sets must be In Progress). Standard fix for "I worked in the wrong set."

Platform answer: attachments are encrypted at rest via instance encryption / Edge Encryption / Column Level Encryption depending on licensing — you configure encryption contexts rather than hand-rolling crypto. If asked about scripted crypto for a payload, GlideEncrypter (legacy, global scope) or CertificateEncryption APIs exist — with the caveat that key management, not the API call, is the real problem.

A normal set is a flat list of captured changes, promoted alone. A batch is a parent-child hierarchy of sets promoted as one coordinated unit — ordering handled by the platform, granularity preserved. Think "commit" vs "release."

Notifications

17 questions

An email/SMS/push message the platform sends when something happens — record conditions match, an event fires, or it's triggered from a flow. It keeps humans in the loop: assignments, approvals, breaches, resolutions. Defined in sysevent_email_action.

Embed ${mail_script:script_name} in the message body — the platform runs the sys_script_email record with that name and injects its template.print() output at that position.

The hidden reference code (Ref:MSG…) appended to outbound mails. When someone replies, inbound processing uses the watermark to match the reply to the originating record and update it — threading without asking users to keep subject lines intact. Remove watermarks and reply-to-update breaks.

  1. Record conditions — when a record on the table is inserted/updated matching the condition.
  2. Event — fires when a registered event lands in the event queue (from gs.eventQueue()).
  3. Triggered from automation — Flow Designer "Send Email"/notification actions, workflows, or scripts.

In a mail script via the email object: email.addAddress('cc', 'a@x.com', 'Name'); and email.addAddress('bcc', ...). Declaratively, recipients configured in the notification go to To; CC/BCC control is a mail-script job (or inbound of a distribution list address).

By default, the user whose action caused the notification doesn't receive it (you don't need mail about your own comment). Checking Send to event creator includes them anyway. Classic gotcha when testing: "my notification doesn't work" — because the tester triggered it themselves.

The notification's own message fields take precedence; the template fills in only where the notification is blank. So a notification with its own subject but empty body uses its subject and the template's body.

The reusable outer frame of the message — header, footer, branding, logo — into which the notification's content is placed. Layouts keep every mail on brand without repeating boilerplate in each notification body.

gs.eventQueue('incident.escalated',   // event name (registered)
              current,                // GlideRecord the event is about
              current.getValue('assigned_to'),   // parm1
              current.getValue('priority'));     // parm2

Name, record, parm1, parm2 (both free-text strings, retrievable as event.parm1/parm2 in notifications and script actions), plus an optional queue name as a fifth argument.

gs.eventQueueScheduled(name, record, parm1, parm2, gdt) — the event (and thus its notification) processes at the GlideDateTime you pass. Alternatives: a Flow with "Wait until" a date field, or a scheduled job that fires events for matching records.

current (triggering record), template (template.print() writes into the body), email (set subject, add addresses), email_action (the notification definition), event (with parm1/parm2 when event-driven).

Outlook's rendering engine ignores much modern CSS. Approach: simplify to table-based layout and inline styles, avoid background images/flex, retest via actual sends to Outlook (not just preview), and use the notification preview plus a test mailbox matrix. Keep one plain-text alternative for the worst clients.

  1. Check System Mailboxes → Outbox/Sent/Failed: was it generated at all?
  2. If not generated: notification conditions, "send to event creator," recipient fields empty, notification inactive, weight suppression.
  3. If generated but not delivered: email logs, SMTP status, instance email enabled (glide.email.smtp.active), spam filtering on the receiving side.
  4. Check user preferences (Notification settings / unsubscribed) and device channels.

When several notifications for the same record+recipient would send in one flush, only the highest weight goes; the rest are suppressed. Weight 0 always sends. Use it to stop a user getting three emails for one update (assigned + commented + priority changed).

The event's second argument must be the record the notification's table expects. From the incident BR, query the related incident_task GlideRecord and pass that: gs.eventQueue('inc_task.notify', grIncidentTask, parm1, parm2). Event table and notification table must line up, or conditions on the notification never match.

Inbound actions on a table run in Order; when one with Stop processing matches and completes, lower-priority inbound actions are skipped for that email. It prevents a mail being interpreted twice (e.g. as both a reply-update and a new-record create).

Digesting batches multiple triggers of the same notification for one recipient into a single periodic summary email instead of a message per occurrence — opt-in per notification ("Allow digest") and controlled by the user's subscription preferences. The answer to "our watchers get 40 emails a day."