Harden bindData against mass assignment#15947
Conversation
Default data binding now denies properties unless they are explicitly marked bindable or named by @BindAllowed on action parameters. The legacy grails.databinding.legacyBindableDefault flag keeps old opt-out binding behavior while preserving bindable:false and domain special-property exclusions. Assisted-by: Sisyphus-Junior:openai/gpt-5.5 codex
There was a problem hiding this comment.
Pull request overview
This PR hardens Grails’ mass data binding to mitigate mass-assignment (CWE-915) by switching to an explicit allowlist model for bindData defaults and controller action auto-binding, while providing a legacy opt-out for migration.
Changes:
- Default binding behavior becomes deny-by-default unless properties are explicitly opted in (primarily via
bindable: trueallowlists generated at compile time). - Introduces
@BindAllowed([...])for controller action parameters to provide action-specific binding allowlists. - Adds a migration switch (
grails.databinding.legacyBindableDefault=true), updates documentation, and expands test coverage for the new defaults and edge cases (including emptyinclude).
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| grails-web-databinding/src/main/groovy/org/grails/web/databinding/DefaultASTDatabindingHelper.java | Generates both default and legacy binding allowlist fields during AST injection. |
| grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java | Enforces deny-by-default include behavior and adds legacy-default config support. |
| grails-web-databinding/src/main/groovy/grails/web/databinding/DataBinder.groovy | Treats explicit empty include as “bind nothing” to match new semantics. |
| grails-web-databinding/src/main/groovy/grails/web/databinding/BindAllowed.java | Adds @BindAllowed annotation for controller action parameter allowlists. |
| grails-test-suite-web/src/test/groovy/org/grails/web/servlet/BindDataMethodTests.groovy | Updates and extends bindData tests for deny-by-default and empty-include behavior. |
| grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/SomeValidateableClass.groovy | Updates constraints to explicitly opt properties into binding. |
| grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/NonValidateableCommand.groovy | Adds constraints to opt properties into binding under the new default model. |
| grails-test-suite-web/src/test/groovy/org/grails/web/commandobjects/CommandObjectsSpec.groovy | Adds @BindAllowed coverage and updates command-object constraints for new defaults. |
| grails-test-suite-web/src/test/groovy/org/grails/web/binding/DefaultASTDatabindingHelperDomainClassSpecialPropertiesSpec.groovy | Expands coverage for default deny behavior and legacy flag behavior in domain inheritance/special properties. |
| grails-doc/src/en/ref/Controllers/bindData.adoc | Documents new default allowlist behavior, empty-include semantics, and @BindAllowed. |
| grails-doc/src/en/ref/Constraints/bindable.adoc | Updates bindable semantics to reflect explicit opt-in model and legacy migration option. |
| grails-doc/src/en/guide/upgrading.adoc | Adds upgrading notes for the new data binding defaults and migration flag. |
| grails-doc/src/en/guide/theWebLayer/controllers/dataBinding.adoc | Updates guide note for empty include behavior and documents allowlist options. |
| grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java | Threads @BindAllowed allowlists into command object initialization during action parameter binding. |
| grails-controllers/src/main/groovy/grails/artefact/Controller.groovy | Adds an overload of initializeCommandObject that accepts allowlisted bind properties and applies them during binding. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| includeList = includeListCache.get(objectClass); | ||
| } else { | ||
| final Field whiteListField = objectClass.getDeclaredField(DefaultASTDatabindingHelper.DEFAULT_DATABINDING_WHITELIST); | ||
| final Field whiteListField = objectClass.getDeclaredField(whiteListFieldName); |
|
The issue described regarding |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15947 +/- ##
================================================
+ Coverage 0 19.5852% +19.5852%
- Complexity 0 308 +308
================================================
Files 0 61 +61
Lines 0 3472 +3472
Branches 0 601 +601
================================================
+ Hits 0 680 +680
- Misses 0 2662 +2662
- Partials 0 130 +130 🚀 New features to boost your workflow:
|
Keep legacy bind-all as the default so scaffolded apps and existing tests keep working. Secure mode is enabled with grails.databinding.denyByDefault=true. @BindAllowed and bindable:false remain available. Walk declared-field hierarchy for whitelist lookup. Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]
🚨 TestLens detected 9 failed tests 🚨Here is what you can do:
Test SummaryCI - Groovy Joint Validation Build / build_grails > :grails-databinding:test
CI - Groovy Joint Validation Build / build_grails > :grails-test-examples-spring-security-core-integration-test-app:integrationTest
CI - Groovy Joint Validation Build / build_grails > :grails-test-examples-spring-security-core-misc-functional-test-app-group:integrationTest
CI / Build Grails-Core (windows-latest, 25) > :grails-databinding:test
CI / Build Grails-Core Rerunning all Tasks (ubuntu-latest, 21) > :grails-databinding:test
🏷️ Commit: 6c85e2b Test FailuresDataBindingConfigurationSpec > test custom ValueConverter are ordered if defined with @Order (:grails-databinding:test in CI / Build Grails-Core (windows-latest, 25))DataBindingConfigurationSpec > test customize data binding for the types which have standard ValueConverters using @Order (:grails-databinding:test in CI / Build Grails-Core (windows-latest, 25))DataBindingConfigurationSpec > test custom ValueConverter are ordered if defined with @Order (:grails-databinding:test in CI / Build Grails-Core Rerunning all Tasks (ubuntu-latest, 21))DataBindingConfigurationSpec > test customize data binding for the types which have standard ValueConverters using @Order (:grails-databinding:test in CI / Build Grails-Core Rerunning all Tasks (ubuntu-latest, 21))DataBindingConfigurationSpec > test custom ValueConverter are ordered if defined with @Order (:grails-databinding:test in CI - Groovy Joint Validation Build / build_grails)DataBindingConfigurationSpec > test customize data binding for the types which have standard ValueConverters using @Order (:grails-databinding:test in CI - Groovy Joint Validation Build / build_grails)SecuredControllerSpec > test login as sherlock, sherlock belongs to detective groups. All detectives have the role ADMIN (:grails-test-examples-spring-security-core-misc-functional-test-app-group:integrationTest in CI - Groovy Joint Validation Build / build_grails)SpringSecurityServiceIntegrationSpec > update role (:grails-test-examples-spring-security-core-integration-test-app:integrationTest in CI - Groovy Joint Validation Build / build_grails)
SpringSecurityServiceIntegrationSpec > update role when invalid (:grails-test-examples-spring-security-core-integration-test-app:integrationTest in CI - Groovy Joint Validation Build / build_grails)Muted TestsSelect tests to mute in this pull request:
Reuse successful test results:
Click the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app. |
Description
What was found
secureBindDatabindDatainsteadWhat changed
grails.databinding.denyByDefault=true@BindAllowed([...])on controller action parametersbindable: true/falsestill honored;falsealways winsExplicitly out of scope
nullMissingMigration
grails.databinding.denyByDefault: truestatic constraints = { title bindable: true }bindData book, params, [include: ['title']]def update(@BindAllowed(['title']) Book book)Related
secureBindDataapproach; close once this landsnullMissing/ stale-data clearingContributor Checklist
Issue and Scope
bindData/ auto-binding only.8.0.x.Code Quality
Licensing and Attribution
ai-generated-starting-pointlabel applied.Documentation
Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]