Skip to content

Fix kyuubi sessions to return 404 for non-existent sessions and add security improvements#7319

Open
Yutong-1424 wants to merge 4 commits intoapache:masterfrom
Yutong-1424:hotfix/yutong/fix_kyuubi_sessions
Open

Fix kyuubi sessions to return 404 for non-existent sessions and add security improvements#7319
Yutong-1424 wants to merge 4 commits intoapache:masterfrom
Yutong-1424:hotfix/yutong/fix_kyuubi_sessions

Conversation

@Yutong-1424
Copy link

Why are the changes needed?

This PR addresses two important issues with the Kyuubi sessions API:

  1. Incorrect HTTP status codes: When attempting to delete a non-existent session, the API currently returns HTTP 500 (Internal Server Error), which is misleading. A non-existent resource should return HTTP 404 (Not Found) to properly indicate the resource doesn't exist.

  2. Security and privacy concerns:

    • The /api/v1/sessions endpoint returns all sessions for all users, potentially exposing sensitive information about other users' sessions
    • Session configuration data may contain sensitive credentials (e.g., passwords, tokens) that should be redacted before being returned to clients

How was this patch tested?

Unit tests were added to verify both changes:

  1. Test for 404 response on delete non-existent session: Added delete_non-existent_admin_session_returns_404 test in AdminResourceSuite.scala that verifies deleting a non-existent session returns HTTP 404 instead of 500.

  2. Test for config redaction: Added get /sessions returns redacted spark confs test in SessionsResourceSuite.scala that:

    • Creates a session with a sensitive configuration value (spark.password)
    • Retrieves the session list via GET /api/v1/sessions
    • Verifies that the sensitive value is redacted (replaced with asterisks) rather than returned in plain text

Was this patch authored or co-authored using generative AI tooling?

No

@Yutong-1424
Copy link
Author

Hi @turboFei, could you help to review this pull request

Response.ok(s"Session $sessionHandleStr is closed successfully.").build()
} catch {
case e: org.apache.kyuubi.KyuubiSQLException
if e.getMessage != null && e.getMessage.startsWith("Invalid ") =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation is tricky.
Maybe you can check session exists directly,
fe.be.sessionManager.getSessionOption()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @turboFei ! That's good point, using getSessionOption() is much cleaner than catching the exception and matching on the message string. I'll update both AdminResource and SessionsResource to check session existence directly before calling closeSession. Will push the fix shortly.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses two issues in the Kyuubi sessions REST API: (1) returning HTTP 404 instead of 500 when deleting a non-existent session, and (2) privacy/security improvements — filtering /api/v1/sessions to only show the current user's sessions and redacting sensitive config values from API responses using the existing kyuubi.server.redaction.regex mechanism.

Changes:

  • Add try/catch around closeSession calls in both SessionsResource and AdminResource to map KyuubiSQLException("Invalid ...") to HTTP 404 via NotFoundException
  • Add user-based filtering in SessionsResource.sessions() and redact sensitive session configuration in ApiUtils.sessionData and ApiUtils.sessionEvent using Utils.redact with SERVER_SECRET_REDACTION_PATTERN
  • Add unit tests verifying the 404 behavior (AdminResourceSuite) and conf redaction (SessionsResourceSuite)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/ApiUtils.scala Adds config redaction to sessionEvent and sessionData methods; contains a type error: Option(redactionPattern) double-wraps an already-Option[Regex] value
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/SessionsResource.scala Adds 404 handling for closeSession and user-scoped filtering in sessions(); API description comment is now inaccurate
kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala Adds 404 handling for admin closeSession; implementation looks correct
kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala New test for 404 on deleting a non-existent session; looks correct
kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala New test for conf redaction; has two bugs: missing SERVER_SECRET_REDACTION_PATTERN config, and wrong assertion using forall(_ == '*') vs actual replacement text "*********(redacted)"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

def sessionData(session: KyuubiSession): SessionData = {
val sessionEvent = session.getSessionEvent
val redactionPattern = session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN)
val redactedConf = Utils.redact(Option(redactionPattern), session.conf.toSeq).toMap.asJava
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same type error as in sessionEvent: Utils.redact(Option(redactionPattern), ...) passes Option[Option[Regex]] where Option[Regex] is required. The redactionPattern variable is already Option[Regex] (from conf.get(SERVER_SECRET_REDACTION_PATTERN)), so it should be passed directly without the Option() wrapping.

Copilot uses AI. Check for mistakes.
@@ -55,8 +55,12 @@ private[v1] class SessionsResource extends ApiRequestContext with Logging {
description = "get the list of all live sessions")
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @ApiResponse description still says "get the list of all live sessions" but the implementation now filters sessions to only return those belonging to the currently authenticated user. The description should be updated to reflect this behavior change, e.g., "get the list of live sessions for the current user".

Suggested change
description = "get the list of all live sessions")
description = "get the list of live sessions for the current user")

Copilot uses AI. Check for mistakes.
val sessionConf = sessions.find(_.getIdentifier == sessionHandle).get.getConf

assert(sessionConf.get(sensitiveKey) != sensitiveValue)
assert(sessionConf.get(sensitiveKey).forall(_ == '*'))
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test asserts sessionConf.get(sensitiveKey).forall(_ == '*'), but the actual redaction replacement text in Kyuubi is "*********(redacted)" (defined as REDACTION_REPLACEMENT_TEXT in CommandLineUtils), which contains characters other than '*'. This assertion would fail even when redaction works correctly. The assertion should instead compare against REDACTION_REPLACEMENT_TEXT or at minimum check that the value is not equal to the original sensitive value and not null.

Suggested change
assert(sessionConf.get(sensitiveKey).forall(_ == '*'))
assert(sessionConf.get(sensitiveKey) != null)

Copilot uses AI. Check for mistakes.
assert(sessionHandle.toString.equals(operations.head.getSessionId))
}

test("get /sessions returns redacted spark confs") {
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test does not configure SERVER_SECRET_REDACTION_PATTERN (via conf.set(SERVER_SECRET_REDACTION_PATTERN, ...)) before asserting that the sensitive config value is redacted. Since this config entry has no default value (createOptional), the redaction mechanism will not activate, and sensitiveValue will be returned unredacted. The test needs to set the redaction pattern (e.g., "(?i)password".r) as part of the test setup or override the server config in WithKyuubiServer.

Suggested change
test("get /sessions returns redacted spark confs") {
test("get /sessions returns redacted spark confs") {
// Configure redaction so that sensitive configs like spark.password are masked
conf.set(KyuubiConf.SERVER_SECRET_REDACTION_PATTERN, "(?i)password".r)

Copilot uses AI. Check for mistakes.
session.getSessionEvent.map(event =>
session.getSessionEvent.map { event =>
val redactionPattern = session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN)
val redactedConf = Utils.redact(Option(redactionPattern), event.conf.toSeq).toMap.asJava
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call Utils.redact(Option(redactionPattern), ...) has a type error. session.sessionManager.getConf.get(SERVER_SECRET_REDACTION_PATTERN) already returns Option[Regex] because OptionalConfigEntry[Regex] extends ConfigEntry[Option[Regex]]. Wrapping it in another Option(...) produces Option[Option[Regex]], which is a type mismatch with Utils.redact's first parameter type Option[Regex]. This would cause a compile-time error.

The correct call should pass redactionPattern directly (without Option()), since it's already an Option[Regex].

Copilot uses AI. Check for mistakes.
- Fix type mismatch
- Update test assertions for correct redaction verification
- Configure redaction pattern at suite level
} catch {
case e: org.apache.kyuubi.KyuubiSQLException
if e.getMessage != null && e.getMessage.startsWith("Invalid ") =>
throw new NotFoundException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please apply same change here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, Thanks!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please review these changes again, to see if everything is good. @turboFei

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants