diff --git a/src/iceberg/catalog/rest/types.cc b/src/iceberg/catalog/rest/types.cc index 00053743f..58a2a423e 100644 --- a/src/iceberg/catalog/rest/types.cc +++ b/src/iceberg/catalog/rest/types.cc @@ -259,8 +259,8 @@ Status OAuthTokenResponse::Validate() const { std::string lower_type = token_type; std::ranges::transform(lower_type, lower_type.begin(), ::tolower); if (lower_type != "bearer" && lower_type != "n_a") { - return ValidationFailed(R"(Unsupported token type: {} (must be "bearer" or "N_A"))", - token_type); + // Keep OAuth response values out of errors that callers may log. + return ValidationFailed(R"(Unsupported token type (must be "bearer" or "N_A"))"); } return {}; } diff --git a/src/iceberg/test/auth_manager_test.cc b/src/iceberg/test/auth_manager_test.cc index 0bae43061..0e82da4bf 100644 --- a/src/iceberg/test/auth_manager_test.cc +++ b/src/iceberg/test/auth_manager_test.cc @@ -659,6 +659,35 @@ Result> RefreshingSession( } // namespace +TEST(OAuth2UtilTest, TokenResponseErrorsDoNotExposeResponseValues) { + const std::array responses{ + R"({"access_token":"sensitive-response-value",)", + R"({"access_token":"sensitive-response-value"})", + R"({"access_token":{"value":"sensitive-response-value"},"token_type":"bearer"})", + R"({"access_token":"access","token_type":"sensitive-response-value"})", + R"({"access_token":"access","token_type":"","refresh_token":"sensitive-response-value"})", + R"({"access_token":"","token_type":"bearer","refresh_token":"sensitive-response-value"})"}; + ICEBERG_UNWRAP_OR_FAIL(auto properties, + AuthProperties::FromProperties( + {{"credential", "client:secret"}, + {"oauth2-server-uri", "https://auth.example/token"}})); + auto session = AuthSession::MakeDefault({}); + RefreshHttpClient client; + const auto check_error = [](const Result& result) { + ASSERT_FALSE(result.has_value()); + EXPECT_THAT(result.error().message, + ::testing::Not(::testing::HasSubstr("sensitive-response-value"))); + }; + for (const auto& body : responses) { + client.response = [&] { return HttpResponse(200, body); }; + check_error(OAuth2Util::FetchToken(client, *session, properties)); + check_error(OAuth2Util::ExchangeToken( + client, *session, {}, "subject-token", AuthProperties::kAccessTokenType, + std::nullopt, std::nullopt, "catalog", "https://auth.example/token", {})); + } + EXPECT_EQ(client.requests, responses.size() * 2); +} + TEST(OAuth2SessionTest, RefreshesAndReschedulesThroughPublicFactory) { MockExecutor executor; std::array, 2> queued;