Remoting Policies -- item-based CLM, command allowlists, and approved scripts for SPE remoting
Summary
SPE remoting runs all remote scripts in FullLanguage mode with no granular control over what commands or modules a remote caller can use. This issue tracks the implementation of item-based Remoting Policies that bring defense-in-depth to SPE's remoting surface.
Guiding principles:
- Opt-in, not opt-out -- no predetermined default policies ship with SPE. Admins create policies as needed.
- Items-only -- all policies are Sitecore content items. No XML config profiles.
- Allowlist-only -- only "Allowed Commands" exist. No blocklist/allowlist mode toggle.
- Secure by default -- ConstrainedLanguage is the default; Full Language is an opt-in checkbox.
- Fail-closed -- API Keys without a policy deny all requests. Deleted policies deny all requests.
Remoting Policy Template
Path: /sitecore/templates/Modules/PowerShell Console/Remoting/Remoting Policy
Section: General
| Field |
Type |
Default |
Description |
| Full Language |
Checkbox |
Unchecked |
Controls the PowerShell language mode. When unchecked, scripts run in ConstrainedLanguage mode. |
| Audit Level |
Droplist |
Violations |
Controls how much detail is logged per policy. See Audit Level Details below. |
Section: Inline Script Restrictions
| Field |
Type |
Description |
| Allowed Commands |
Multi-Line Text |
Permitted commands for inline scripts, one per line. Does not apply to Web API scripts invoked by reference. |
Section: Script Approval
| Field |
Type |
Description |
| Approved Scripts |
Multilist |
Web API scripts permitted for execution by reference. Uses a script data source that queries all Web API integration points across modules. |
Enforcement model
| Endpoint |
Gate |
What happens |
| Inline script |
Allowed Commands |
Each command is checked against the allowlist. Unlisted commands are rejected. |
| v2 by reference |
Approved Scripts |
The script must be in the policy's Approved Scripts list. Unlisted scripts are denied. |
Both endpoints respect the Full Language checkbox for language mode.
Audit Level Details
Audit logging uses a two-pass model. Events that occur before a policy is resolved (authentication, API key validation, hard security rejections) are always logged unconditionally regardless of the policy's Audit Level setting. Events that occur after policy resolution are gated by the policy's Audit Level.
Always logged (unconditional, pre-policy or security-critical)
These events always appear in the audit log:
| Action |
Description |
requestReceived |
Incoming remoting request (entry point) |
authenticated |
Successful authentication |
bearerAuthSuccess |
JWT bearer auth succeeded |
bearerAuthFailed |
JWT bearer auth failed |
bearerAuthError |
JWT bearer auth error |
apiKeyValidated |
API key matched |
authRejected |
Authentication rejected |
userUnauthorized |
User not authorized for service |
pathTraversalBlocked |
Path traversal attempt blocked |
apiKeyDenied |
API key has no policy assigned |
Violations (default) -- security violations and data transfers
Includes everything unconditional, plus:
| Action |
Description |
commandBlocked |
Command blocked by policy allowlist |
scriptRejected |
Script blocked by service-level allowlist |
scriptRejectedByPolicy |
Script blocked by policy allowlist |
scriptNotApproved |
Script not in approved scripts list |
throttled |
Request throttled |
fileUploaded |
File uploaded to server |
fileDownloaded |
File downloaded from server |
mediaUploaded |
Media item uploaded |
mediaUpdated |
Media item updated |
mediaDownloaded |
Media item downloaded |
File and media transfer events are included at this level because data exfiltration is a security-relevant event.
Standard -- operational audit trail
Includes everything in Violations, plus:
| Action |
Description |
throttleBypassed |
Throttle limit exceeded but request allowed (bypass mode) |
scriptStarting |
Script execution starting |
scriptApproved |
Script approved by policy |
policyAudit |
Policy evaluation summary |
scriptCompleted |
Script execution completed |
scriptFailed |
Script execution failed with error |
sessionCleanup |
Server-side session disposed |
connectionTest |
Connection test endpoint invoked |
Full -- verbose diagnostics
Includes everything in Standard, plus additional detail on each request:
| Action |
Description |
scriptDetail |
Script body length and language mode at execution start |
responseDetail |
Output format and HTTP status code at completion |
requestDetail |
Query parameter count (values are not logged) |
Note: Full is intended for troubleshooting and development environments. It should not be enabled in production long-term due to log volume.
Design notes
- The
Unrestricted sentinel policy (used for legacy shared-secret sessions without an API Key) defaults to Standard to ensure the most privileged access path has visibility.
- The
Denied sentinel policy (used when a referenced policy is invalid) defaults to Violations.
- If the Audit Level field value cannot be parsed, the system defaults to
Violations and logs a warning.
Remoting API Keys
API keys are managed as Sitecore content items. Each key binds a shared secret to a remoting policy.
Path: /sitecore/templates/Modules/PowerShell Console/Remoting/Remoting API Key
| Field |
Type |
Description |
| Enabled |
Checkbox |
Activate/deactivate this key. |
| Shared Secret |
Single-Line Text |
The authentication secret for this key. Minimum 32 characters for HS256, 48 for HS384, 64 for HS512 (per RFC 7518). Use a random hex string, e.g. 64 characters. |
| Policy |
Droplink |
Required. The remoting policy that governs this key. |
| Impersonate User |
Single-Line Text |
Required. The Sitecore identity for the remote session. The JWT Name claim is ignored - this field is the authoritative identity. |
| Request Limit |
Integer |
Maximum requests allowed within the throttle window. |
| Throttle Window |
Integer |
Time window in seconds for rate limiting. |
| Throttle Action |
Droplist |
Action when the throttle limit is exceeded. Block (default) rejects with HTTP 429. Bypass allows the request through and logs the event. |
Consumer workflow
The standard SPE remoting module handles API Key authentication automatically. No manual JWT construction needed. The server matches the shared secret to the API Key, reads the bound policy, and enforces it.
Key behaviors
- Policy is mandatory -- an API Key with no policy assigned returns 403 on all requests.
- Impersonate User is mandatory -- an API Key without an Impersonate User returns 403. The JWT Name claim is not trusted for identity when an API key is matched; the Impersonate User field is the authoritative identity for the session.
- Per-key throttling -- rate limiting is enforced per API Key with X-RateLimit response headers.
- Throttle action -- Block rejects with 429; Bypass allows through with audit log (monitoring mode).
- Timing-safe comparison -- all secret comparisons use SecureCompare.FixedTimeEquals.
- Minimum secret length -- enforced per RFC 7518 Section 3.2: 32 characters for HS256, 48 for HS384, 64 for HS512. Secrets shorter than the HMAC hash output reduce the effective key space. A warning is logged at API key load time and requests are rejected at validation time if the secret is too short.
Security Model
Authentication flow
- Bearer token validated (JWT signature checked against HS256 allowlist)
- API Key items checked first; legacy config shared secret as fallback
- Identity established (user from JWT Name claim or API Key impersonation)
Authorization flow
- If API Key is present but has no policy: 403 (denied)
- If no API Key: Unrestricted (backward compatible for legacy shared-secret sessions)
- Policy resolved from API Key's Droplink field, then endpoint-specific enforcement applies
Fail-closed defaults
- API Keys with no policy: denied
- Deleted or unparseable policy items: denied
- The system defaults to no access in every ambiguous state
Response headers
| Header |
Description |
| X-SPE-LanguageMode |
Active language mode for the session |
| X-SPE-Restriction |
Set on 403 responses (command-blocked or policy-blocked) |
| X-SPE-BlockedCommand |
The specific command that was blocked |
| X-SPE-Policy |
The remoting policy that blocked the command |
| X-RateLimit-Limit |
Request limit for the throttle window |
| X-RateLimit-Remaining |
Requests remaining in the current window |
| X-RateLimit-Reset |
Unix timestamp when the throttle window resets |
| Retry-After |
Seconds to wait before retrying (only on 429) |
Test Coverage
Integration tests (Remoting.RemotingPolicies.*)
- Policy language mode enforcement (via API Key)
- Command allowlist enforcement (inline endpoint)
- Execution escape prevention (dynamic invocation blocking)
- Backward compatibility (legacy shared secret = unrestricted)
- Policy enforcement after exceptions
- Dynamic invocation rejection
- Publish-Item allowed by policy
- API Key without policy is denied
- Script approval (v2 endpoint -- approved vs unapproved scripts)
Integration tests (Remoting.Throttle.*)
- Block action -- requests within limit succeed
- Block action -- exceeding limit returns 429 with rate-limit headers
- Bypass action -- requests within limit succeed
- Bypass action -- exceeding limit returns 200 with audit log
- Default action (empty field) -- behaves like Block
Remaining Work
Documentation (tracked in Book repo)
Known Issues
Remoting Policies -- item-based CLM, command allowlists, and approved scripts for SPE remoting
Summary
SPE remoting runs all remote scripts in FullLanguage mode with no granular control over what commands or modules a remote caller can use. This issue tracks the implementation of item-based Remoting Policies that bring defense-in-depth to SPE's remoting surface.
Guiding principles:
Remoting Policy Template
Path:
/sitecore/templates/Modules/PowerShell Console/Remoting/Remoting PolicySection: General
Section: Inline Script Restrictions
Section: Script Approval
Enforcement model
Both endpoints respect the Full Language checkbox for language mode.
Audit Level Details
Audit logging uses a two-pass model. Events that occur before a policy is resolved (authentication, API key validation, hard security rejections) are always logged unconditionally regardless of the policy's Audit Level setting. Events that occur after policy resolution are gated by the policy's Audit Level.
Always logged (unconditional, pre-policy or security-critical)
These events always appear in the audit log:
requestReceivedauthenticatedbearerAuthSuccessbearerAuthFailedbearerAuthErrorapiKeyValidatedauthRejecteduserUnauthorizedpathTraversalBlockedapiKeyDeniedViolations (default) -- security violations and data transfers
Includes everything unconditional, plus:
commandBlockedscriptRejectedscriptRejectedByPolicyscriptNotApprovedthrottledfileUploadedfileDownloadedmediaUploadedmediaUpdatedmediaDownloadedFile and media transfer events are included at this level because data exfiltration is a security-relevant event.
Standard -- operational audit trail
Includes everything in Violations, plus:
throttleBypassedscriptStartingscriptApprovedpolicyAuditscriptCompletedscriptFailedsessionCleanupconnectionTestFull -- verbose diagnostics
Includes everything in Standard, plus additional detail on each request:
scriptDetailresponseDetailrequestDetailNote: Full is intended for troubleshooting and development environments. It should not be enabled in production long-term due to log volume.
Design notes
Unrestrictedsentinel policy (used for legacy shared-secret sessions without an API Key) defaults toStandardto ensure the most privileged access path has visibility.Deniedsentinel policy (used when a referenced policy is invalid) defaults toViolations.Violationsand logs a warning.Remoting API Keys
API keys are managed as Sitecore content items. Each key binds a shared secret to a remoting policy.
Path:
/sitecore/templates/Modules/PowerShell Console/Remoting/Remoting API KeyConsumer workflow
The standard SPE remoting module handles API Key authentication automatically. No manual JWT construction needed. The server matches the shared secret to the API Key, reads the bound policy, and enforces it.
Key behaviors
Security Model
Authentication flow
Authorization flow
Fail-closed defaults
Response headers
Test Coverage
Integration tests (Remoting.RemotingPolicies.*)
Integration tests (Remoting.Throttle.*)
Remaining Work
Documentation (tracked in Book repo)
Known Issues