-
-
Notifications
You must be signed in to change notification settings - Fork 71
Description
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:
- JWT
scopeclaim (if present) - API Key item profile (if API key auth used)
- Service-level config
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
itemPathRestrictionssection with a mode (blocklist or allowlist) and a list of Sitecore paths - Prefix matching -- blocking
/sitecore/system/Modules/PowerShell/Settings/Remotingalso blocks all children - Enforcement happens in the Sitecore provider (
PsSitecoreItemProvider) at theWriteItem()andGetChildNames()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
RestrictionProfileis set onScriptSession.ActiveRestrictionProfileby the remoting handler before script execution - The provider reads this via the
$ScriptSessionPowerShell variable - Non-remoting contexts (ISE, console) have no active profile and are unrestricted
- Respects
Enforcementmode:Enforceblocks access with a non-terminating error,Auditonly logs - Respects
AuditLevelfor logging denied access attempts
Known limitation:
- Provider-level enforcement only. Direct .NET API calls like
item.Childrenfrom 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 duringRemoteScriptCallexecution- Trust is checked for both REST and SOAP (
RemoteAutomation.asmx) remoting endpoints - Untrusted scripts are rejected under restricted profiles
- Cache is invalidated automatically via
TrustedScriptSaveHandlerwhen 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:
RemotingApiKeyProviderloads 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 activeX-SPE-BlockedCommand-- reports which command was blockedX-SPE-Profile-- reports the active restriction profile
Client support:
Invoke-RemoteScriptin 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
& $variableare 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.FixedTimeEqualsto 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:
- Language mode enforcement (ConstrainedLanguage restrictions apply)
- Command blocklist/allowlist enforcement
- Module restriction enforcement
- Execution escape prevention (dynamic invocation blocking)
- Remote session prevention under restricted profiles
- JWT scope-to-profile mapping
- Backward compatibility (unrestricted profile matches pre-CLM behavior)
- Exception survival (errors surface correctly through restriction layer)
Additional tests cover profile override lifecycle with cache expiry.