{"repro_id":"REPRO-2026-00165","version":13,"title":"DataEase: JDBC parameter blocklist bypass via Lombok @Data setter exposure","repro_type":"security","status":"published","severity":"medium","cvss_score":6.5,"description":"DataEase exposes a \"Datasource\" feature that lets a logged-in administrator\nadd JDBC connections to MySQL/PostgreSQL/Oracle/etc. To prevent that\nadministrator from setting dangerous JDBC parameters (which can turn a benign\n\"open a database connection\" action into file read or deserialization), the\nserver keeps a server-side **blocklist** of parameter names — at minimum\n`allowLoadLocalInfile`, `autoDeserialize`, `allowMultiQueries`, and similar.\n\nThe blocklist is held on a Java configuration bean. Its class is annotated\nwith **Lombok `@Data`**, which auto-generates a public no-arg setter for\n**every non-final field**, including the blocklist field itself. Spring's\ndefault JSON binding (Jackson) then happily calls that setter when the JSON\nrequest body contains a matching key. An admin (the threat model) submitting\nthe \"add datasource\" form can therefore include a JSON field that overwrites\nthe blocklist with `[]` in the same request that pushes a custom\n`extraParams` value through the JDBC URL.\n\nOnce `allowLoadLocalInfile=true` reaches the JDBC URL, the MySQL JDBC client\nwill honor any `LOCAL INFILE` response from the server it connects to —\nincluding a rogue MySQL listener controlled by the same admin — and read\narbitrary files off the DataEase host into the connection-test response.\n\nThis is step #2 of the public 4-CVE chain (auth bypass → blocklist bypass →\nstacked SQLi → Quartz deserialization RCE).","root_cause":"# RCA Report: CVE-2026-40899\n\n## Summary\n\nDataEase community edition ≤ v2.10.20 allows an authenticated administrator to bypass the server-side JDBC parameter blocklist by exploiting Lombok's `@Data` annotation on datasource configuration classes. The `@Data` annotation auto-generates public setters for all fields, including the `illegalParameters` blocklist field. When Spring's Jackson JSON binder deserializes the incoming datasource configuration, it calls this setter and overwrites the hardcoded blocklist with an attacker-supplied value (e.g., an empty array). As a result, forbidden parameters such as `allowloadlocalinfile=true` can be injected into the JDBC URL, enabling arbitrary file read via a rogue MySQL server.\n\n## Impact\n\n- **Package/component affected**: `core/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java` (and sibling datasource type classes: Pg, Impala, Sqlserver, Db2, H2, CK, Redshift, Mongo)\n- **Affected versions**: DataEase community edition ≤ v2.10.20\n- **Fixed versions**: v2.10.21\n- **Risk level**: Medium (CVSS 3.1: 6.5)\n- **Consequences**: A privileged user can bypass the JDBC parameter blocklist, inject dangerous MySQL parameters (e.g., `allowLoadLocalInfile`), and trigger arbitrary file read from the DataEase server host.\n\n## Root Cause\n\nThe datasource type classes (e.g., `Mysql.java`) are annotated with Lombok `@Data`, which generates a public setter for every non-final field. The field `illegalParameters` holds a hardcoded list of dangerous JDBC parameter names that must be blocked. Because Jackson's default deserialization strategy invokes any public setter that matches a JSON key, an attacker can include `\"illegalParameters\": []` in the same JSON request that defines the datasource. This overwrites the blocklist before the `getJdbc()` validation logic runs, allowing any subsequently supplied `extraParams` to pass validation unchecked.\n\nThe fix commit is `16a950f96089b2a90e37d82304ede714a40902ba` (\"fix: 【漏洞】Arbitrary File Read (Credential Exfiltration)\"). It adds `@JsonIgnore` to the `illegalParameters` field in all affected datasource type classes, preventing Jackson from ever binding user input to that field.\n\n## Reproduction Steps\n\n1. Run `repro/reproduction_steps.sh`\n2. The script:\n   - Pulls the official DataEase Docker images for v2.10.20 (vulnerable) and v2.10.21 (fixed)\n   - Starts each image in `desktop` mode (which bypasses token-based authentication) on separate ports\n   - Waits for the real `/de2api/datasource/types` endpoint to respond\n   - Sends an HTTP POST to `/de2api/datasource/validate` with a Base64-encoded malicious MySQL configuration containing `\"illegalParameters\": []` and `\"extraParams\": \"allowloadlocalinfile=true\"`\n   - Captures and compares the responses\n3. Expected evidence:\n   - **Vulnerable (v2.10.20)**: The server returns a JDBC connection error (`Communications link failure`), proving that `getJdbc()` did **not** reject the forbidden parameter and instead attempted to open a connection.\n   - **Fixed (v2.10.21)**: The server returns `Illegal parameter: allowloadlocalinfile`, proving that the blocklist was enforced and the bypass was blocked.\n\n## Evidence\n\n- `logs/vulnerable_response.json`:\n  ```json\n  {\"code\":40001,\"msg\":\"DEException(code=40001, msg=Communications link failure\\n\\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.)\",\"data\":null}\n  ```\n- `logs/fixed_response.json`:\n  ```json\n  {\"code\":40001,\"msg\":\"DEException(code=40001, msg=Illegal parameter: allowloadlocalinfile)\",\"data\":null}\n  ```\n- `repro/runtime_manifest.json` documents the exact endpoints, payloads, and responses for both versions.\n\n## Recommendations / Next Steps\n\n1. **Primary fix**: Apply `@JsonIgnore` (or equivalent Jackson ignore annotation) to all blocklist/whitelist fields on configuration beans that must not be user-modifiable. This is exactly what the DataEase maintainers did in v2.10.21.\n2. **Defense in depth**: Consider making `illegalParameters` a `private final` field initialized in the constructor or a `static final` constant, so there is no setter at all — even for other deserialization frameworks.\n3. **Upgrade guidance**: Users on DataEase ≤ v2.10.20 should upgrade to v2.10.21 or later immediately.\n4. **Testing recommendations**: Add an integration test that POSTs a datasource configuration containing an `illegalParameters` override to the live `/de2api/datasource/validate` endpoint and asserts that the response is a blocklist rejection, not a connection attempt.\n\n## Additional Notes\n\n- **Idempotency**: `repro/reproduction_steps.sh` has been executed twice consecutively from a clean state and produced the same results both times.\n- **Edge cases / limitations**: The reproduction uses the `desktop` Spring profile to bypass authentication, which is the simplest way to reach the vulnerable endpoint without implementing RSA-encrypted login. This does not affect the validity of the reproduction because the vulnerable code path (Jackson deserialization of `Mysql` followed by `getJdbc()` validation) is identical across all profiles.\n","cve_id":"CVE-2026-40899","cwe_id":"CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes)","source_url":"https://github.com/dataease/dataease","package":{"name":"dataease","ecosystem":"github","affected_versions":"<= v2.10.20","fixed_version":"v2.10.21","tested_vulnerable":"true","tested_patched":"true"},"reproduced_at":"2026-05-25T14:12:06.407615+00:00","duration_secs":6683.564529657364,"tool_calls":487,"turns":408,"handoffs":2,"total_cost_usd":5.872985600000007,"agent_costs":{"judge":0.649015,"repro":3.5724111800000027,"support":0.06849090999999999,"vuln_variant":1.5830685100000008},"cost_breakdown":{"judge":{"claude-opus-4-7":0.649015},"repro":{"accounts/fireworks/models/kimi-k2p6":3.5724111800000027},"support":{"accounts/fireworks/models/kimi-k2p6":0.06849090999999999},"vuln_variant":{"accounts/fireworks/models/kimi-k2p6":1.5830685100000008}},"quality":{"confidence":"high","idempotent_verified":true,"community_verifications":0},"published_at":"2026-05-25T14:12:08.772642+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":5173,"category":"analysis"},{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":9798,"category":"reproduction_script"},{"path":"bundle/vuln_variant/rca_report.md","filename":"rca_report.md","size":8757,"category":"analysis"},{"path":"bundle/vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":11396,"category":"reproduction_script"},{"path":"bundle/context.json","filename":"context.json","size":2660,"category":"other"},{"path":"bundle/metadata.json","filename":"metadata.json","size":876,"category":"other"},{"path":"bundle/ticket.md","filename":"ticket.md","size":4558,"category":"ticket"},{"path":"bundle/repro/start_dataease.sh","filename":"start_dataease.sh","size":5099,"category":"other"},{"path":"bundle/repro/logs/repro_run_3.log","filename":"repro_run_3.log","size":267,"category":"log"},{"path":"bundle/repro/logs/repro_run.log","filename":"repro_run.log","size":1535,"category":"log"},{"path":"bundle/repro/logs/repro_run_2.log","filename":"repro_run_2.log","size":602,"category":"log"},{"path":"bundle/repro/logs/start_dataease.log","filename":"start_dataease.log","size":98,"category":"log"},{"path":"bundle/repro/logs/repro_run_compose.log","filename":"repro_run_compose.log","size":668,"category":"log"},{"path":"bundle/repro/logs/repro_run_final.log","filename":"repro_run_final.log","size":450,"category":"log"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":1192,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":2881,"category":"other"},{"path":"bundle/vuln_variant/test_variants_fixed.sh","filename":"test_variants_fixed.sh","size":7938,"category":"other"},{"path":"bundle/vuln_variant/root_cause_equivalence.json","filename":"root_cause_equivalence.json","size":1952,"category":"other"},{"path":"bundle/vuln_variant/patch_analysis.md","filename":"patch_analysis.md","size":5739,"category":"documentation"},{"path":"bundle/vuln_variant/variant_manifest.json","filename":"variant_manifest.json","size":4688,"category":"other"},{"path":"bundle/vuln_variant/validation_verdict.json","filename":"validation_verdict.json","size":3466,"category":"other"},{"path":"bundle/logs/vulnerable_response.json","filename":"vulnerable_response.json","size":224,"category":"other"},{"path":"bundle/logs/fixed_response.json","filename":"fixed_response.json","size":104,"category":"other"},{"path":"bundle/logs/variant_syntax_test.log","filename":"variant_syntax_test.log","size":843,"category":"log"},{"path":"bundle/logs/variant_repro_run.log","filename":"variant_repro_run.log","size":728,"category":"log"},{"path":"bundle/logs/variant_test_run.log","filename":"variant_test_run.log","size":680,"category":"log"},{"path":"bundle/logs/variant_test_fix.py","filename":"variant_test_fix.py","size":2070,"category":"script"},{"path":"bundle/logs/repro_retry.log","filename":"repro_retry.log","size":668,"category":"log"},{"path":"bundle/logs/dataease-vuln_startup.log","filename":"dataease-vuln_startup.log","size":21610,"category":"log"},{"path":"bundle/logs/variant_syntax_test2.log","filename":"variant_syntax_test2.log","size":843,"category":"log"},{"path":"bundle/logs/variant_test.py","filename":"variant_test.py","size":2130,"category":"script"},{"path":"bundle/logs/dataease-vuln_crash.log","filename":"dataease-vuln_crash.log","size":61,"category":"log"},{"path":"bundle/logs/dataease-fix-variant_startup.log","filename":"dataease-fix-variant_startup.log","size":23082,"category":"log"},{"path":"bundle/logs/repro_retry.pid","filename":"repro_retry.pid","size":6,"category":"other"},{"path":"bundle/logs/exploit.py","filename":"exploit.py","size":976,"category":"script"},{"path":"bundle/logs/dataease-fix_startup.log","filename":"dataease-fix_startup.log","size":60,"category":"log"},{"path":"bundle/test/file.txt","filename":"file.txt","size":13,"category":"other"}]}