spring-projects / spring-projects/spring-vault
SecretLeaseCreatedEvent throws NullPointerException for secrets containing null values (regression in 4.0.0)
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:
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:
- Restore the null-tolerant copy from 3.2.0, or
- Drop null-valued entries before copying, or
- At minimum, route the failure through
onErrorso it surfaces as a
VaultExceptionnaming the offending path.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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