Skip to content

feat(security): default-off kill-switch for user-generated dynamic code execution#2860

Merged
simonredfern merged 3 commits into
OpenBankProject:developfrom
hongwei1:fix/stability-batch5
Jul 13, 2026
Merged

feat(security): default-off kill-switch for user-generated dynamic code execution#2860
simonredfern merged 3 commits into
OpenBankProject:developfrom
hongwei1:fix/stability-batch5

Conversation

@hongwei1

Copy link
Copy Markdown
Contributor

Summary

  • User-generated code compiled and executed via DynamicUtil (Dynamic Resource Docs, Connector Methods, Dynamic Message Docs, ABAC Rules) runs with no effective sandbox and no way for an operator to disable it. Two runtime facts make this worse than it looks:
    • SecurityManager was removed in JDK 24+ (JEP 486); Sandbox.runInSandbox is now a pass-through with no permission enforcement.
    • The GraalVM JS context used by createJsFunction is built with allowHostAccess(HostAccess.ALL) and allowHostClassLookup(_ => true), i.e. full host access.
    • These endpoints are reachable by any user holding the relevant bank-level entitlement, not only instance super-admins.
  • Adds a default-off kill-switch using the existing (previously unused) allow_user_generated_scala_code prop. DynamicUtil.dynamicCodeExecutionEnabled resolves it with explicit-prop-wins semantics; absent the prop it's on in test/dev and off in production.
  • compileScalaCode is split into a guarded public entry point and a private compileScalaCodeUnchecked used only by DynamicUtil.Validation's own props-driven config parsing (operator config, not user code) — otherwise the app would fail to boot with the switch off, since that parsing happens at object-init time.
  • Adds early guards to the create/update endpoints for connector methods, dynamic resource docs, dynamic message docs and ABAC rules (Http4s400.scala, Http4s600.scala) so a disabled instance returns a clean 400 OBP-50020 instead of surfacing a downstream compile failure.
  • Dynamic Entities and Swagger Dynamic Endpoints are intentionally not gated — they never compile or execute user-supplied code (pure DB-backed schema/CRUD and Swagger-parsing/HTTP-proxy respectively).
  • Documents the prop and its security rationale in sample.props.template.

Test plan

  • mvn install -pl .,obp-commons -am -DskipTests — BUILD SUCCESS
  • mvn test-compile -pl obp-api -am — main + test sources compile clean
  • New suite DynamicCodeKillSwitchTest: predicate on/off, each of the three guarded endpoint chokepoints on/off, and a Dynamic Entity regression check (unaffected when the switch is off)
  • Existing dynamic-code suites stay green under testMode: DynamicUtilTest, ConnectorMethodTest, AbacRuleTests, DynamicResourceDocTest, DynamicMessageDocTest, ResourceDocsTest — 114 succeeded, 0 failed (3 canceled are pre-existing JDK24+ SecurityManager-unavailable skips, unrelated to this change)
  • git grep -n 'allow_user_generated_scala_code' obp-api/src confirms the prop is now read in code and documented

hongwei1 added 3 commits July 7, 2026 14:03
DynamicUtil.compileScalaCode, createJsFunction and createJavaFunction
compile and execute user-supplied Scala/JS/Java with no sandbox on
JDK 24+ (SecurityManager removal, JEP 486) and GraalVM contexts built
with HostAccess.ALL, so any bank-level entitlement holder able to reach
one of the dynamic-code management endpoints could achieve RCE.

Adopt the existing but previously unused allow_user_generated_scala_code
prop as the master switch, defaulting to off. compileScalaCode is split
into a guarded public entry point and an unchecked variant used only by
DynamicUtil.Validation's own props-driven config parsing, so operator
config still compiles and the app boots with the switch off.
…h the kill-switch

Add an early check to the create/update endpoints for connector
methods, dynamic resource docs, dynamic message docs and ABAC rules so
a disabled instance returns a clean 400 (OBP-50020) instead of
surfacing the compile failure further down the stack. Dynamic Entities
and Swagger Dynamic Endpoints are intentionally left unguarded since
they never compile or execute user-supplied code.

Document the prop in sample.props.template with the security rationale,
and add DynamicCodeKillSwitchTest covering the predicate directly, each
guarded endpoint on/off, and a regression check that Dynamic Entities
stay unaffected.
@sonarqubecloud

Copy link
Copy Markdown

@simonredfern simonredfern merged commit 73113ed into OpenBankProject:develop Jul 13, 2026
12 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