Skip to content

Constrained Language Mode profiles for SPE remoting security #1426

@michaellwest

Description

@michaellwest

Constrained Language Mode (CLM) for SPE Remoting

Summary

SPE remoting previously ran all remote scripts in FullLanguage mode with no granular control over what commands or modules a remote caller could use. This issue tracks the implementation of Constrained Language Mode (CLM) support, bringing defense-in-depth to SPE's remoting surface.

The core implementation is complete across 7 commits on feature/clm. What remains is documentation and a migration guide.

What was built:

  • Restriction Profiles -- named security policies that control language mode, command access, module access, item path access, and audit level
  • Item path restrictions -- profiles can deny or allow access to Sitecore content tree paths (prefix match), protecting sensitive items like API Keys from remote scripts
  • Item-based profile overrides -- Sitecore content items that extend config-defined profiles with additional restrictions or allowances
  • Trusted Scripts -- a binary trust model where scripts must be explicitly trusted (by item) to run under restricted profiles
  • Remoting API Keys -- item-based API key management with per-key profile binding, rate limiting, and throttling
  • Fail-closed security -- unknown profiles resolve to DenyAll, dynamic invocations are rejected in blocklist mode, timing-safe secret comparison throughout

Restriction Profiles

A Restriction Profile defines the security policy applied to a remote session. Profiles are defined in Sitecore config and can be extended via content items.

Config-defined profiles (4 predefined):

Profile Language Mode Command Policy Item Path Policy Description
unrestricted FullLanguage No restrictions No restrictions Default -- backward compatible
read-only ConstrainedLanguage Blocklist (write commands blocked) Blocklist (Remoting settings denied) Safe for reporting
read-only-strict ConstrainedLanguage Allowlist (only approved commands) Blocklist (Remoting settings denied) Locked-down reporting
content-editor ConstrainedLanguage Blocklist (dangerous commands blocked) Allowlist (content, media, layout only) Content operations only

Profile properties:

  • Language Mode -- FullLanguage or ConstrainedLanguage
  • Command Restrictions -- blocklist or allowlist of PowerShell commands
  • Module Restrictions -- which modules may be loaded
  • Item Path Restrictions -- blocklist or allowlist of Sitecore content tree paths (prefix match)
  • Audit Level -- controls logging verbosity
  • Enforcement Mode -- active enforcement or audit-only

Item-based profile overrides

Content items under /sitecore/system/Modules/PowerShell/Settings/Remoting/Restriction Profiles/ can extend a config-defined base profile. Overrides are additive only -- they can add blocked or allowed commands/paths but cannot remove base restrictions.

  • Template: Restriction Profile (under /templates/Modules/PowerShell Console/Remoting/)
  • Fields: Base Profile, Additional Blocked Commands, Additional Allowed Commands, Additional Blocked Paths (Treelist), Additional Allowed Paths (Treelist), Audit Level Override, Enabled
  • Caching: HttpRuntime.Cache with TTL-based expiry

Profile resolution order:

  1. JWT scope claim (if present)
  2. API Key item profile (if API key auth used)
  3. Service-level config
  4. unrestricted (fallback)

Unknown profile references resolve to DenyAll (fail closed).


Item Path Restrictions

Profiles can restrict which Sitecore items remote scripts can access. This prevents constrained callers from reading sensitive items like API Keys, trust configuration, or other protected content tree areas.

How it works:

  • Each profile has an optional itemPathRestrictions section with a mode (blocklist or allowlist) and a list of Sitecore paths
  • Prefix matching -- blocking /sitecore/system/Modules/PowerShell/Settings/Remoting also blocks all children
  • Enforcement happens in the Sitecore provider (PsSitecoreItemProvider) at the WriteItem() and GetChildNames() egress points
  • Catches all access patterns: by path, by ID/GUID, by query, and via Get-ChildItem

Default restrictions:

Profile Mode Paths
unrestricted none --
read-only blocklist /sitecore/system/Modules/PowerShell/Settings/Remoting
read-only-strict blocklist /sitecore/system/Modules/PowerShell/Settings/Remoting
content-editor allowlist /sitecore/content, /sitecore/media library, /sitecore/layout

Config example:

<profile name="read-only" ...>
  <itemPathRestrictions mode="blocklist">
    <blockedPaths>
      <path>/sitecore/system/Modules/PowerShell/Settings/Remoting</path>
    </blockedPaths>
  </itemPathRestrictions>
</profile>

Item-based path overrides:

Override items can add paths via Treelist fields (Additional Blocked Paths, Additional Allowed Paths). Treelist stores item GUIDs, so restrictions survive item renames and moves. GUIDs are resolved to paths at merge time.

Enforcement:

  • The active RestrictionProfile is set on ScriptSession.ActiveRestrictionProfile by the remoting handler before script execution
  • The provider reads this via the $ScriptSession PowerShell variable
  • Non-remoting contexts (ISE, console) have no active profile and are unrestricted
  • Respects Enforcement mode: Enforce blocks access with a non-terminating error, Audit only logs
  • Respects AuditLevel for logging denied access attempts

Known limitation:

  • Provider-level enforcement only. Direct .NET API calls like item.Children from within a script are not restricted. This is acceptable because ConstrainedLanguage mode already restricts arbitrary method invocation in constrained profiles.

Trusted Scripts

Trust is binary -- a script is either trusted or untrusted. There are no trust levels.

Trusted scripts are managed entirely through content items under /sitecore/system/Modules/PowerShell/Settings/Remoting/Trusted Scripts/. Each trust item references one or more scripts via a Treelist field, so a single trust item can cover a group of related scripts.

Trust item properties:

  • Script -- Treelist field referencing PowerShell Script Library items
  • Allowed Profiles -- restricts which restriction profiles the trust applies to
  • Enabled -- checkbox for easy toggling without deleting the item

How trust is evaluated:

  • ScriptTrustRegistry.EvaluateTrust() is called during RemoteScriptCall execution
  • Trust is checked for both REST and SOAP (RemoteAutomation.asmx) remoting endpoints
  • Untrusted scripts are rejected under restricted profiles
  • Cache is invalidated automatically via TrustedScriptSaveHandler when trust items are saved

Design rationale:

  • Content hash verification was dropped for item-based trust -- the admin who can edit a script can also edit the trust item, making hash checks redundant
  • Config-based trust (XML trustedScripts, scopeRestrictions, Generate-TrustedScripts.ps1) was removed entirely in favor of the simpler item-based approach

Remoting API Keys

API keys are now managed as Sitecore content items instead of relying solely on a single shared secret in config.

API Key item properties:

  • Shared Secret -- the authentication secret for this key
  • Enabled -- checkbox to activate/deactivate
  • Profile -- which restriction profile applies to sessions using this key
  • Impersonate User -- optional user context for the remote session
  • Request Limit -- maximum requests allowed within the throttle window
  • Throttle Window -- time window for rate limiting

Provider behavior:

  • RemotingApiKeyProvider loads and caches API key items with TTL expiry
  • Auth flow checks API Key items first, then falls back to legacy config shared secret
  • Duplicate shared secret values across keys trigger warnings
  • All secret comparisons use timing-safe SecureCompare.FixedTimeEquals

Rate limiting:

  • Per-key throttle enforcement based on Request Limit and Throttle Window
  • Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • HTTP 429 returned when limit exceeded

Restriction headers:

  • X-SPE-Restriction -- indicates restrictions are active
  • X-SPE-BlockedCommand -- reports which command was blocked
  • X-SPE-Profile -- reports the active restriction profile

Client support:

  • Invoke-RemoteScript in the remoting module handles 429 (rate limit) and 403 (restriction) responses

Security Hardening

Several cross-cutting security improvements were made:

  • Item path restrictions -- profiles can deny access to sensitive content tree paths, protecting API Keys and security configuration from remote scripts
  • Dynamic invocation rejection -- expressions like & $variable are rejected when a blocklist is active, preventing blocklist bypass
  • Fail-closed defaults -- unknown or misconfigured profile references resolve to DenyAll
  • Timing-safe comparison -- all shared secret comparisons use SecureCompare.FixedTimeEquals to prevent timing attacks
  • SOAP coverage -- restriction profile enforcement applies to RemoteAutomation.asmx (SOAP) in addition to REST endpoints
  • Nested folder support -- all Settings subtrees (Restriction Profiles, Trusted Scripts, API Keys) support nested folders for organization
  • Unknown profile warnings -- ProfileOverrideProvider warns when override items reference non-existent profile names

Template Organization

All new templates are organized under a Remoting parent:

/sitecore/templates/Modules/PowerShell Console/Remoting/
  Restriction Profile
  Trusted Script
  Remoting API Key

Settings items live under:

/sitecore/system/Modules/PowerShell/Settings/Remoting/
  Restriction Profiles/
  Trusted Scripts/
    SPE/          (ships with 16 core trusted scripts)
  API Keys/

Implementation History

Commit Phase Description
1 Core CLM Infrastructure RestrictionProfile model, RestrictionProfileManager, ScriptTrustRegistry, 4 predefined profiles, integration tests (8 groups)
2 Item-Based Profile Overrides Restriction Profile template, ProfileOverrideProvider, cache with TTL, integration tests
3 Zero-Trust Script Allowlist Generate-TrustedScripts.ps1, AST-based export extraction, SHA256 hashing (later superseded by Phase 6 simplification)
4 Item-Based Trust Management Trusted Script template, profile-bound trust, TrustedScriptSaveHandler, cache invalidation
5 Remoting API Keys API Key template, RemotingApiKeyProvider, throttling, rate limit headers, restriction headers, client-side 429/403 handling
6 Simplify Trust Model Binary trust (removed Trust Level), Treelist script references, removed config-based trust entirely, SOAP enforcement, dynamic invocation rejection, fail-closed DenyAll, Enabled checkboxes, template reorganization
7 Item Path Restrictions ItemPathRestrictions on profiles (blocklist/allowlist with prefix matching), provider-level enforcement in PsSitecoreItemProvider, Treelist override fields, default restrictions protecting Remoting settings

Remaining Work

Documentation and Migration Guide

  • Migration guide for existing installations (config shared secret to API Key items)
  • Admin documentation for creating and managing restriction profiles
  • Admin documentation for trusted script management
  • Admin documentation for item path restrictions
  • README updates for remoting security model

Test Coverage

Integration tests cover 8 groups:

  1. Language mode enforcement (ConstrainedLanguage restrictions apply)
  2. Command blocklist/allowlist enforcement
  3. Module restriction enforcement
  4. Execution escape prevention (dynamic invocation blocking)
  5. Remote session prevention under restricted profiles
  6. JWT scope-to-profile mapping
  7. Backward compatibility (unrestricted profile matches pre-CLM behavior)
  8. Exception survival (errors surface correctly through restriction layer)

Additional tests cover profile override lifecycle with cache expiry.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions