spring-projects / spring-projects/spring-vault

SecretLeaseCreatedEvent throws NullPointerException for secrets containing null values (regression in 4.0.0)

Open Beginner friendly
#1,018 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
306
Forks
202
PR merge metrics
No merged PRs in 30d

Description

Affected versions

Regression introduced in 4.0.0. Confirmed still present in 4.1.0.
Working in 3.2.0.

Description

SecretLeaseCreatedEvent copies the secret data with Map.copyOf(secrets),
which rejects null values with a NullPointerException:

https://github.com/spring-projects/spring-vault/blob/main/spring-vault-core/src/main/java/org/springframework/vault/core/lease/event/SecretLeaseCreatedEvent.java

public SecretLeaseCreatedEvent(RequestedSecret requestedSecret, Lease lease, Map<String, Object> secrets) {
    super(requestedSecret, lease);
    this.secrets = Map.copyOf(secrets);   // NPE if any value is null
}

3.2.0 used a null-tolerant copy:

this.secrets = Collections.unmodifiableMap(new LinkedHashMap<>(secrets));

SecretLeaseRotatedEvent extends SecretLeaseCreatedEvent and delegates to
this constructor, so the rotation path fails identically.

Reproducer

No Vault instance required — the constructor alone reproduces it:

Map<String, Object> secrets = new LinkedHashMap<>();
secrets.put("access_key", "SOME_KEY");
secrets.put("secret_key", "SOME_VALUE);
secrets.put("session_token", null);

// throws NullPointerException on 4.0.0+, succeeds on 3.2.0
new SecretLeaseCreatedEvent(RequestedSecret.renewable("aws/creds/my-role"), Lease.none(), secrets);
Real-world trigger

Vault's AWS secrets engine returns a null session token for the iam_user
credential type, because no STS call is involved:

{
  "data": {
    "access_key": "AKIA...",
    "secret_key": "...",
    "session_token": null
  }
}
How it surfaces

Via Spring Cloud Vault (spring-cloud-vault-config 5.0.2), with
spring.cloud.vault.aws.enabled=true:

SecretLeaseContainer.doGetSecrets      // reads aws/creds/<role>
SecretLeaseContainer.start             // -> onSecretsObtained(...)
SecretLeaseEventPublisher.onSecretsObtained  // -> dispatch(new SecretLeaseCreatedEvent(...))
SecretLeaseCreatedEvent.<init>         // NullPointerException

doGetSecrets has a try/catch (RuntimeException) that routes failures to
onError, but it wraps only the HTTP read — onSecretsObtained is invoked
afterwards, outside it. The NPE therefore escapes unwrapped rather than
reaching any LeaseErrorListener, so the application fails to start with a
bare NullPointerException and no indication of which secret or key caused it.

Expected behaviour

Either:

  1. Restore the null-tolerant copy from 3.2.0, or
  2. Drop null-valued entries before copying, or
  3. At minimum, route the failure through onError so it surfaces as a
    VaultException naming the offending path.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with spring-vault-core/src/main/java/org/springframework/vault/core/lease/event/SecretLeaseCreatedEvent.java and inspect its constructor and the SecretLeaseRotatedEvent delegation. Reproduce the failure with a map containing a null session_token, then verify that creating these events succeeds without a NullPointerException and add or update coverage for the null-valued secret case.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.