Skip to content

fix: EXPOSED-1012 Do not include the column value in valueFromDB error messages - #2965

Open
Lee, Hyo jun (devyourown) wants to merge 2 commits into
JetBrains:mainfrom
devyourown:fix/EXPOSED-1012-value-in-error-message
Open

Lee, Hyo jun (devyourown) wants to merge 2 commits into
JetBrains:mainfrom
devyourown:fix/EXPOSED-1012-value-in-error-message

Conversation

@devyourown

@devyourown Lee, Hyo jun (devyourown) commented Oct 10, 2026 •

Copy link
Copy Markdown

Description

Summary of the change: valueFromDB error messages no longer contain the column value itself, only the expected type and the runtime type of the value. The error is now an UnexpectedValueTypeException (a subtype of IllegalStateException).

Detailed description:

  • Why: The "Unexpected value" errors in ColumnType.kt interpolated $value into the message. If the value's toString() exposes sensitive data (the ticket's case: a decrypted secret behind a ColumnTransformer), the plaintext ends up in logs and stack traces. Because the exception type was a plain IllegalStateException, callers had no safe way to catch it and sanitize the message.
  • What:
    • Added UnexpectedValueTypeException(message) : IllegalStateException in exposed-core (org.jetbrains.exposed.v1.exceptions), with KDoc explaining why the value is omitted. Since it extends IllegalStateException, existing catch (e: IllegalStateException) blocks keep working.
    • Added a private helper unexpectedValue(expected, value): Nothing in ColumnType.kt that builds the message from expected and value::class.qualifiedName only.
    • Replaced the 18 error("Unexpected value ... $value ...") sites with the helper. The two NaN checks in DecimalColumnType were left as they are, because they print the literal text NaN, not the value.
    • Updated exposed-core.api (apiDump) for the new public class.
    • Added a test in BinaryColumnTypeTests that reproduces the leak with a binary(...).transform(...) column whose wrapped type is a data class, and asserts that the message does not contain the plaintext but does contain the runtime type name. The test fails on main with Unexpected value Secret(plain=123-45-6789) of type ...Secret.
  • How: A single Nothing-returning helper keeps all sites consistent, so the message format can only be changed in one place. The message format is expected type: <X>, actual type: <qualified name>.

A note on the exception type: the ticket asked for a more specific exception than IllegalStateException, which is why the new class is added. If you would rather not grow the public API for this, I am happy to drop the class and keep IllegalStateException; the message change is independent of it.

One observation while writing the test (not changed in this PR): assigning a wrapped Kotlin-side value with ResultRow.set on a transform-ed column stores it raw, so the next get passes the wrapped object to the inner column's valueFromDB. That is the path the reproduction uses, and it is likely how the value in the ticket ended up in the message. I left that behaviour as is since it is a separate question.

Reproduction credit: Marko Cevrljakovic (@MARKOCEVRLJAKOVIC) (marecevrljakovic on YouTrack) shared a reproduction project on the ticket (https://gist.github.com/MARKOCEVRLJAKOVIC/9fde02311037e98b898b4f7a5c221700), which confirmed the path through ColumnTransformer. Thank you.

AI assistance: I used an LLM (Claude) to explore the codebase, locate the 20 call sites, draft the test scaffold and parts of this description. The design (type-only message, single helper, new exception type) and the code changes were written by me, and I ran all checks locally.


Type of Change

  • Bug fix
  • New feature
  • Documentation update

Updates/remove existing public API methods:

  • Is breaking change

Affected databases:

  • MariaDB
  • Mysql5
  • Mysql8
  • Oracle
  • Postgres
  • Redshift
  • SqlServer
  • H2
  • SQLite

(The change is in exposed-core and not dialect-specific. Tests were run locally with H2 only: :exposed-tests:test_h2_v2 1059 tests, 0 failures.)

Checklist

  • Unit tests are in place
  • The build is green (including the Detekt check)
  • All public methods affected by my PR has up to date API docs
  • Documentation for my change is up to date (no user-facing docs mention these messages)

Related Issues

EXPOSED-1012: https://youtrack.jetbrains.com/issue/EXPOSED-1012

…r messages

The "Unexpected value" errors thrown by valueFromDB interpolated the raw value
into the message, so a value whose toString() exposes sensitive data (e.g. a
decrypted secret behind a transform) could end up in logs and stack traces.
All such sites now go through a single helper that reports only the expected
type and the runtime type of the value, and throw UnexpectedValueTypeException
(a subtype of IllegalStateException, so existing catch blocks still work).

This branch has not been deployed

No deployments
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.

1 participant