Skip to content

Conversation

@feeblefakie
Copy link
Contributor

This is an automated backport of the following:

Please merge this PR after all checks have passed.

@gemini-code-assist
Copy link

Summary of Changes

Hello @feeblefakie, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request backports a critical fix to prevent NullPointerExceptions that could arise from clients being misconfigured with digital signatures. The primary approach involves enhancing the secret management system by introducing a dedicated MissingSecretException. This exception is now thrown when a secret cannot be found, replacing the previous behavior of returning null. This change ensures more robust error handling, provides immediate and specific feedback on secret-related issues, and improves the overall stability of the system by preventing unexpected runtime errors.

Highlights

  • New Exception for Missing Secrets: Introduced a new MissingSecretException to explicitly handle cases where a requested secret is not found, providing clearer error messages and preventing NullPointerExceptions.
  • Refactored Secret Lookup Logic: Modified the SecretManager and ScalarSecretRegistry to throw MissingSecretException when a secret is absent, rather than returning null. This changes the flow of secret registration and lookup to be exception-driven.
  • New Error Code and Status: Added a SECRET_NOT_FOUND error (code 415) to CommonError and StatusCode enums for standardized error reporting.
  • Updated Integration and Unit Tests: Adjusted existing tests and added new ones to validate the new exception-based error handling for missing secrets and scenarios where clients are misconfigured with digital signatures.
  • Removed Obsolete Constant File: The Constants.java file, which contained the KEYS_JSON_NAME constant, has been removed, indicating a cleanup or refactoring of how this constant is managed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a backport that fixes a NullPointerException when a client is misconfigured for digital signatures. The core change is modifying SecretRegistry.lookup to throw a MissingSecretException instead of returning null when a secret is not found. This is a good improvement for robustness. The related code, including tests, has been updated accordingly.

I've added two comments:

  1. A high-severity comment on SecretManager.register pointing out a pre-existing race condition and recommending an atomic operation for registration as a follow-up.
  2. A medium-severity comment on ScalarSecretRegistry.lookup with a suggestion to make the code more concise and idiomatic.

Comment on lines 59 to 66
public void register(SecretEntry entry) {
SecretEntry existing = registry.lookup(entry.getKey());
if (existing != null) {
try {
registry.lookup(entry.getKey());
throw new DatabaseException(CommonError.SECRET_ALREADY_REGISTERED);
} catch (MissingSecretException e) {
registry.bind(entry);
}
registry.bind(entry);
}

Choose a reason for hiding this comment

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

high

This implementation of register uses a "Look-Before-You-Leap" (LBYL) approach by first calling lookup and then bind. This pattern is vulnerable to a race condition in a concurrent environment. Two threads could both check for the existence of a secret, find it missing, and then both attempt to bind it, with the second one overwriting the first.

A more robust approach would be to use an atomic 'put-if-absent' operation in the underlying SecretRegistry. The bind method in SecretRegistry could be modified to use a conditional Put (e.g., with PutIfNotExists in ScalarDB) and throw an exception if the secret already exists. This would make the registration atomic.

The register method here would then simplify to just calling registry.bind(entry) and handling the potential exception.

Since this race condition existed before this change, and fixing it would require changes outside the scope of this file, I recommend creating a follow-up task to address this atomicity issue.

Comment on lines +83 to +93
Result result;
try {
return storage.get(get).map(this::toSecretEntry).orElse(null);
result =
storage
.get(get)
.orElseThrow(() -> new MissingSecretException(CommonError.SECRET_NOT_FOUND));
} catch (ExecutionException e) {
throw new DatabaseException(CommonError.GETTING_SECRET_KEY_FAILED, e, e.getMessage());
}

return toSecretEntry(result);

Choose a reason for hiding this comment

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

medium

The logic in this try-catch block can be made more concise and idiomatic by better leveraging the Optional API. You can chain map and orElseThrow to achieve the same result with less code.

    try {
      return storage
          .get(get)
          .map(this::toSecretEntry)
          .orElseThrow(() -> new MissingSecretException(CommonError.SECRET_NOT_FOUND));
    } catch (ExecutionException e) {
      throw new DatabaseException(CommonError.GETTING_SECRET_KEY_FAILED, e, e.getMessage());
    }

@jnmt jnmt merged commit 1fd7a96 into 3 Oct 29, 2025
11 checks passed
@jnmt jnmt deleted the 3-pull-302 branch October 29, 2025 10:03
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.

2 participants