## Ticket: CVE-2026-40899 - DataEase JDBC Parameter Blocklist Bypass via Lombok `@Data`

**CVE**: CVE-2026-40899 | **CWE-915 / CWE-470** | **CVSS 3.1**: 6.5 (Medium)
**Vendor / Product**: DataEase (FIT2CLOUD) - Spring Boot data visualization platform
**Repository**: https://github.com/dataease/dataease
**Affected**: `<= v2.10.20` | **Fixed**: `v2.10.21`
**Writeup**: https://www.ox.security/blog/from-auth-bypass-to-rce-a-4-vulnerability-exploit-chain-in-dataease/
**NVD**: https://nvd.nist.gov/vuln/detail/CVE-2026-40899

### Impact

DataEase exposes a "Datasource" feature that lets a logged-in administrator
add JDBC connections to MySQL/PostgreSQL/Oracle/etc. To prevent that
administrator from setting dangerous JDBC parameters (which can turn a benign
"open a database connection" action into file read or deserialization), the
server keeps a server-side **blocklist** of parameter names — at minimum
`allowLoadLocalInfile`, `autoDeserialize`, `allowMultiQueries`, and similar.

The blocklist is held on a Java configuration bean. Its class is annotated
with **Lombok `@Data`**, which auto-generates a public no-arg setter for
**every non-final field**, including the blocklist field itself. Spring's
default JSON binding (Jackson) then happily calls that setter when the JSON
request body contains a matching key. An admin (the threat model) submitting
the "add datasource" form can therefore include a JSON field that overwrites
the blocklist with `[]` in the same request that pushes a custom
`extraParams` value through the JDBC URL.

Once `allowLoadLocalInfile=true` reaches the JDBC URL, the MySQL JDBC client
will honor any `LOCAL INFILE` response from the server it connects to —
including a rogue MySQL listener controlled by the same admin — and read
arbitrary files off the DataEase host into the connection-test response.

This is step #2 of the public 4-CVE chain (auth bypass → blocklist bypass →
stacked SQLi → Quartz deserialization RCE).

### Affected / Fixed

| | Version |
|--|--|
| **Vulnerable** | DataEase community ≤ `v2.10.20` |
| **Fixed** | `v2.10.21` (https://github.com/dataease/dataease/releases/tag/v2.10.21) |

### Where the patch lives

Compare the v2.10.20 and v2.10.21 trees:

```
git clone https://github.com/dataease/dataease external/dataease
cd external/dataease
git diff v2.10.20 v2.10.21 -- '**/*.java' | grep -E '(@Data|blocklist|blackList|allowLoadLocalInfile|extraParams)'
```

Look for:
- Removal of `@Data` (replaced with `@Getter` + explicit setters) on the
  configuration class that holds the blocklist.
- A new defensive copy / `Collections.unmodifiableList` around the blocklist
  field.
- Tightening of the datasource controller's `@RequestBody` DTO so the
  blocklist field is no longer bindable from incoming JSON.

The `core-backend/src/main/java/io/dataease/datasource/...` subtree is the
most likely location; the article specifically calls out the `@Data`
annotation as the smoking gun.

### Reproduction plan

1. Build/run vulnerable DataEase + MySQL via the official `docker-compose`
   that pins images to `v2.10.20` (or use the published image
   `dataease/dataease:v2.10.20`).
2. Authenticate as the default `admin / DataEase@123456`.
3. Spin up a tiny TCP listener on a network reachable from the DataEase
   container that speaks the MySQL handshake far enough to issue a
   `LOCAL INFILE` packet for `/etc/passwd`. (The same trick is documented
   widely as the "rogue MySQL server" pattern; an off-the-shelf reference
   implementation is fine — the bug is in DataEase, not in MySQL JDBC's
   correct-by-spec behavior.)
4. POST a datasource that names that listener as host, **and** includes the
   blocklist override field in the same JSON body, **and** sets
   `allowLoadLocalInfile=true` in `extraParams`.
5. Trigger a connection test. The vulnerable build returns the contents of
   `/etc/passwd` (or whatever file the listener requested) in its response /
   logs; the v2.10.21 build rejects the request because the blocklist field
   is no longer settable from JSON.

### Expected reproduction artifacts

- `reproduction_steps.sh` — `docker compose up` (vulnerable then fixed),
  the rogue MySQL listener, the two HTTP requests (vulnerable response =
  file contents; fixed response = 4xx with "forbidden parameter").
- `validation_verdict.json` — `successful` only if **both** halves hold.
- `evidence/` — saved JSON request body, saved JSON response from the
  vulnerable build showing the file contents, fixed-build response with
  the rejection error.
