Skip to content

Groovy sandbox whitelist support#3817

Merged
sumerjabri merged 13 commits intocraftercms:developfrom
jmendeza:bugfix/1156-e-5x
Sep 25, 2025
Merged

Groovy sandbox whitelist support#3817
sumerjabri merged 13 commits intocraftercms:developfrom
jmendeza:bugfix/1156-e-5x

Conversation

@jmendeza
Copy link
Copy Markdown
Member

@jmendeza jmendeza commented Sep 23, 2025

https://github.com/craftersoftware/craftercms/issues/1156
Groovy sandbox whitelist support

Summary by CodeRabbit

  • New Features

    • Optional Groovy script sandbox with whitelist for safer execution.
    • Expanded, curated whitelist of approved APIs available to scripts.
    • Configurable script locations, extensions, and paths for lifecycle and REST scripts.
  • Configuration

    • New settings to enable/disable the sandbox and specify a whitelist file.
    • New setting to enable/disable Groovy Grape auto-download.
    • New property to define the lifecycle scripts classpath.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 23, 2025

Walkthrough

Adds Groovy script sandboxing support with a new constructor and engine setup in GroovyScriptExecutor, introduces whitelist configuration and optional Grape auto-download toggle, updates Spring wiring for sandbox/whitelist and classpath, and expands Groovy sandbox whitelist while adjusting blacklist comments and adding new Studio config properties.

Changes

Cohort / File(s) Summary of Changes
GroovyScriptExecutor & sandbox engine
src/main/java/.../GroovyScriptExecutor.java
Constructor expanded to accept SandboxInterceptor, scripts classpath, plugin classpath, and enableScriptSandbox. Added fields, getScriptEngine(...) with CompilerConfiguration and optional RejectASTTransformsCustomizer/SandboxTransformer. executeScriptString(...) now registers/unregisters sandbox interceptor around evaluation. Constant renamed to GROOVY_ENGINE_NAME.
Spring services wiring
src/main/resources/crafter/studio/studio-services-context.xml
Updated studioGroovyScriptExecutor bean: depends on studioConfiguration, injects sandboxInterceptor, dynamic lifecycle classpath, and enableScriptSandbox. Expanded SandboxInterceptorFactory with whitelist enable/path properties.
Overlay context (Grape toggle)
src/main/resources/crafter/studio/extension/services-overlay-context.xml
Added grapeAutoDownloadDisabler MethodInvokingFactoryBean to call groovy.grape.Grape.setEnableAutoDownload using studio.scripting.grapes.download.enabled.
Studio configuration
src/main/resources/crafter/studio/studio-config.yaml
Added properties: studio.scripting.lifecycle.classpath, studio.scripting.sandbox.whitelist.enable, studio.scripting.sandbox.whitelist.path, studio.scripting.grapes.download.enabled, studio.scripting.script.extension, studio.scripting.script.path.format, studio.scripting.classes.path, studio.scripting.rest.path.
Groovy sandbox lists
src/main/resources/crafter/studio/groovy/blacklist, src/main/resources/crafter/studio/groovy/whitelist
Blacklist: removed an initial comment line. Whitelist: large addition of allowed Groovy/Java API surface, including JSON, IO, collections, time, regex, logging, and Crafter lifecycle APIs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Executor as GroovyScriptExecutor
  participant Sandbox as SandboxInterceptor
  participant Engine as GroovyScriptEngineImpl

  Caller->>Executor: executeScriptString(siteId, script, model)
  alt enableScriptSandbox == true
    Executor->>Sandbox: register()
  end
  Executor->>Executor: getScriptEngine(siteId, model)
  activate Executor
  note over Executor: Build CompilerConfiguration<br/>+ optional RejectASTTransformsCustomizer<br/>+ SandboxTransformer when enabled
  Executor->>Engine: instantiate with configured GroovyClassLoader
  deactivate Executor
  Executor->>Engine: eval(script, bindings)
  Engine-->>Executor: result or exception
  alt enableScriptSandbox == true
    Executor->>Sandbox: unregister()
  end
  Executor-->>Caller: return result or propagate error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sumerjabri

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change — adding Groovy sandbox whitelist and related sandboxing support across configuration and executor code — and is clear, concise, and relevant to the modified files.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/main/resources/crafter/studio/studio-config.yaml (1)

1074-1077: Consider default-enabling sandbox whitelist for stronger security posture.

The whitelist is disabled by default (false) while the blacklist is enabled. For a more secure-by-default configuration, consider enabling the whitelist by default, especially since the whitelist file is already provided.

-studio.scripting.sandbox.whitelist.enable: true
+studio.scripting.sandbox.whitelist.enable: true
src/main/java/org/craftercms/studio/impl/v1/script/GroovyScriptExecutor.java (1)

61-76: Consider adding null checks for sandboxInterceptor.

While the constructor accepts sandboxInterceptor which could be null (based on Spring wiring), the code in executeScriptString already handles null checking. However, consider documenting this behavior or adding validation.

 protected ScriptEngine getScriptEngine(String siteId, Map<String, Object> model) {
     ScriptEngineManager factory = new ScriptEngineManager();
     factory.setBindings(new SimpleBindings(model));
     GroovyScriptEngineImpl scriptEngine = (GroovyScriptEngineImpl) factory.getEngineByName(GROOVY_ENGINE_NAME);
     CompilerConfiguration config = new CompilerConfiguration();
     if (enableScriptSandbox) {
+        // Configure sandbox transformers for secure script execution
         config.addCompilationCustomizers(new RejectASTTransformsCustomizer(), new SandboxTransformer());
     }
src/main/resources/crafter/studio/extension/services-overlay-context.xml (1)

87-98: Grape auto-download controlled by config — property present; add debug logging (optional)
studio.scripting.grapes.download.enabled is defined at src/main/resources/crafter/studio/studio-config.yaml:1095 (false); the MethodInvokingFactoryBean in src/main/resources/crafter/studio/extension/services-overlay-context.xml uses studioConfiguration.getProperty(...) so the setting will be applied. Add brief debug logging when calling Grape.setEnableAutoDownload to aid troubleshooting Groovy dependency issues.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4169f80 and a9a90d7.

📒 Files selected for processing (6)
  • src/main/java/org/craftercms/studio/impl/v1/script/GroovyScriptExecutor.java (3 hunks)
  • src/main/resources/crafter/studio/extension/services-overlay-context.xml (2 hunks)
  • src/main/resources/crafter/studio/groovy/blacklist (0 hunks)
  • src/main/resources/crafter/studio/groovy/whitelist (1 hunks)
  • src/main/resources/crafter/studio/studio-config.yaml (2 hunks)
  • src/main/resources/crafter/studio/studio-services-context.xml (2 hunks)
💤 Files with no reviewable changes (1)
  • src/main/resources/crafter/studio/groovy/blacklist
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-08T23:38:33.963Z
Learnt from: jmendeza
PR: craftercms/studio#3794
File: src/main/resources/crafter/studio/studio-services-context.xml:801-808
Timestamp: 2025-08-08T23:38:33.963Z
Learning: In CrafterCMS Studio's Spring XML configuration, beans can have partial constructor arguments specified in XML while Spring autowires the remaining arguments by type. The DiskMonitor class uses ConstructorProperties annotation which enables Spring to correctly match and inject the missing dependencies even when only some constructor arguments are explicitly provided in the XML configuration.

Applied to files:

  • src/main/resources/crafter/studio/extension/services-overlay-context.xml
📚 Learning: 2025-09-22T21:12:13.850Z
Learnt from: alhambrav
PR: craftercms/studio#3814
File: src/main/api/studio-api.yaml:7-7
Timestamp: 2025-09-22T21:12:13.850Z
Learning: Repo: craftercms/studio — Versioning workflow: maintainers update pom.xml, docs, and runtime version endpoints close to release; PRs that only bump OpenAPI info.version (src/main/api/studio-api.yaml) are acceptable without immediate pom/docs changes.

Applied to files:

  • src/main/resources/crafter/studio/studio-services-context.xml
  • src/main/resources/crafter/studio/studio-config.yaml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Travis CI - Pull Request
🔇 Additional comments (8)
src/main/resources/crafter/studio/studio-config.yaml (2)

1067-1068: LGTM! Good separation of sandbox configuration.

The lifecycle scripts classpath property is appropriately configured with the default value.


1093-1096: Good security practice disabling Grapes auto-download.

Disabling automatic dependency downloads by default prevents potential security vulnerabilities from untrusted or compromised dependencies being pulled into the runtime environment.

src/main/resources/crafter/studio/studio-services-context.xml (2)

1317-1325: Dynamic classpath configuration looks good.

The scriptsClassPath is now dynamically constructed using the studio.scripting.lifecycle.classpath property, providing flexibility for different deployment scenarios.


1530-1533: LGTM! Whitelist configuration properly wired.

The sandboxInterceptor factory bean now correctly accepts whitelist configuration from Studio properties.

src/main/java/org/craftercms/studio/impl/v1/script/GroovyScriptExecutor.java (1)

78-91: Good sandbox lifecycle management.

The sandbox interceptor is properly registered and unregistered in a try-finally block, ensuring cleanup even if script execution throws an exception.

src/main/resources/crafter/studio/groovy/whitelist (3)

1-14: Comprehensive Groovy core type whitelisting.

Good coverage of essential Groovy JSON handling, bindings, and closure operations needed for typical scripting scenarios.


303-309: Security consideration for BigDecimal/BigInteger operations.

While whitelisting multiply and negate operations on BigDecimal/BigInteger is necessary for calculations, be aware that unbounded precision arithmetic operations could potentially be exploited for denial of service through resource exhaustion.

Consider monitoring script execution time and resource usage when these operations are used extensively.


1402-1446: Good Crafter Studio API exposure for lifecycle scripts.

The whitelisted CommonLifecycleApi and DmContentLifeCycleService operations provide appropriate access to lifecycle hooks while maintaining security boundaries. The SLF4J logger exposure is also properly scoped.

@jmendeza
Copy link
Copy Markdown
Member Author

jmendeza commented Sep 24, 2025

@jmendeza jmendeza marked this pull request as ready for review September 24, 2025 01:01
@sumerjabri sumerjabri merged commit dde1736 into craftercms:develop Sep 25, 2025
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants