markhoerth opened a new issue, #14442:
URL: https://github.com/apache/gravitino/issues/14442

   ### Version
   
   main branch, commit bb634defdc5663a8124be5fca1c8d9e52ac096ac (2.0.0-SNAPSHOT)
   
   ### Describe what's wrong
   
   A user creates a catalog (or a schema or fileset) with `secretBindings` and, 
by mistake, gives a binding as a bare string instead of an object, for example 
`"secretBindings": {"x-token": "<secret value>"}`. They expect a 400 that says 
the request is malformed, without repeating the secret.
   
   The request is rejected with 400 `Malformed json request`, but the `stack` 
field of the error response contains the secret value in clear text, quoted 
inside the Jackson exception message. The response goes back to the client and 
through any proxy or client-side logging on the way. The same happens for 
schema create. A malformed JSON document (parse error) and a binding whose 
`plaintext` is a list do not echo the value, so only this deserialization error 
is affected.
   
   Cause:
   
   - `SecretBindingDTO` 
(`common/src/main/java/org/apache/gravitino/dto/secret/SecretBindingDTO.java:41`)
 has no String creator, so Jackson throws `MismatchedInputException` when a 
binding is a string. Jackson includes the offending string value in that 
exception's message (`... from String value ('<value>')`). 
`CatalogCreateRequest`, `SchemaCreateRequest` and `FilesetCreateRequest` all 
hold `Map<String, SecretBindingDTO>`.
   - `JsonMappingExceptionMapper.toResponse` passes the exception into the 
error response 
(`server/src/main/java/org/apache/gravitino/server/web/mapper/JsonMappingExceptionMapper.java:35-37`,
 through 
`server-common/src/main/java/org/apache/gravitino/server/web/Utils.java:112-116`).
   - `ErrorResponse.getStackTrace` prints the full throwable, message included, 
into the `stack` list 
(`common/src/main/java/org/apache/gravitino/dto/responses/ErrorResponse.java:414-424`).
   
   The defect is in Gravitino's own code. Possible fixes: give 
`SecretBindingDTO` a deserializer that rejects a non-object value with a 
message that does not quote it, or have `JsonMappingExceptionMapper` leave out 
the Jackson message and stack (or redact quoted values) for requests whose body 
can carry secret plaintext.
   
   ### Error message and/or stacktrace
   
   ```
   POST /api/metalakes/gvtrepro_kms1/catalogs
   
{"name":"c1","type":"FILESET","provider":"fileset","comment":"","properties":{"location":"file:///tmp/gvtrepro_c1"},"secretBindings":{"x-token":"MY-SECRET-VALUE-123"}}
   
   HTTP 400
   {"code":1001,"type":"IllegalArgumentException","message":"Malformed json 
request","stack":["com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Cannot construct instance of `org.apache.gravitino.dto.secret.SecretBindingDTO` 
(although at least one Creator exists): no String-argument constructor/factory 
method to deserialize from String value ('MY-SECRET-VALUE-123')"," at [Source: 
(org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream);
 line: 1, column: 145] (through reference chain: 
org.apache.gravitino.dto.requests.CatalogCreateRequest[\"secretBindings\"]->java.util.LinkedHashMap[\"x-token\"])","\tat
 
com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)",
 ...]}
   ```
   
   Schema create:
   
   ```
   POST /api/metalakes/gvtrepro_kms1/catalogs/c2/schemas
   
{"name":"s1","comment":"","properties":{},"secretBindings":{"x-token":"MY-SCHEMA-SECRET-789"}}
   
   HTTP 400
   {"code":1001,"type":"IllegalArgumentException","message":"Malformed json 
request","stack":["com.fasterxml.jackson.databind.exc.MismatchedInputException: 
Cannot construct instance of `org.apache.gravitino.dto.secret.SecretBindingDTO` 
(although at least one Creator exists): no String-argument constructor/factory 
method to deserialize from String value ('MY-SCHEMA-SECRET-789')", ...]}
   ```
   
   ### How to reproduce
   
   Gravitino server with a secret provider configured 
(`gravitino.secret.providers=memory`, 
`gravitino.secret.provider.memory.className=org.apache.gravitino.secret.memory.InMemorySecretsProvider`)
 and simple auth.
   
   ```bash
   A="Authorization: Basic $(printf admin:x | base64)"
   G=http://127.0.0.1:8090/api
   H=(-H "$A" -H 'Content-Type: application/json' -H 'Accept: 
application/vnd.gravitino.v1+json')
   ```
   
   1. Create a metalake:
   ```bash
   curl -s "${H[@]}" -X POST $G/metalakes -d 
'{"name":"gvtrepro_kms1","comment":"","properties":{}}'
   ```
   2. Create a catalog with a secret binding given as a string. The response is 
400 and its `stack` contains `MY-SECRET-VALUE-123`:
   ```bash
   curl -s "${H[@]}" -X POST $G/metalakes/gvtrepro_kms1/catalogs -d 
'{"name":"c1","type":"FILESET","provider":"fileset","comment":"","properties":{"location":"file:///tmp/gvtrepro_c1"},"secretBindings":{"x-token":"MY-SECRET-VALUE-123"}}'
   ```
   3. Same for a schema:
   ```bash
   curl -s "${H[@]}" -X POST $G/metalakes/gvtrepro_kms1/catalogs -d 
'{"name":"c2","type":"FILESET","provider":"fileset","comment":"","properties":{"location":"file:///tmp/gvtrepro_c2"}}'
   curl -s "${H[@]}" -X POST $G/metalakes/gvtrepro_kms1/catalogs/c2/schemas -d 
'{"name":"s1","comment":"","properties":{},"secretBindings":{"x-token":"MY-SCHEMA-SECRET-789"}}'
   ```
   
   ### Additional context
   
   - Related: #14437 (`******` accepted as plaintext on create), #14305 and 
#14306 (other secret binding defects). None of them covers the echo.
   - Not present in v1.3.1: secret bindings do not exist at that tag.
   - Found by the 2.0 OSS test suite check 
`test_plaintext_not_echoed_in_errors[binding_as_string]`.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to