Skip to content

Commit b2f820e

Browse files
authored
Fix potential null pointer exceptions (#159)
1 parent fb0664c commit b2f820e

18 files changed

Lines changed: 177 additions & 36 deletions

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ jobs:
4747
gcloud-auth: ${{ secrets.GCLOUD_AUTH }}
4848
env-file-path: .env.integration
4949
- name: integration test
50-
run: env $(cat .env.integration) ./test-suites/integrationTest.sh
50+
run: env $(cat .env.integration) ./test-suites/allTest.sh
51+
env:
52+
TENANT_ID: INTEGRATION-TEST-GCP
53+
NEW_TENANT_ID: INTEGRATION-TEST-AWS
5154

5255
build_examples:
5356
runs-on: ubuntu-22.04

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/BatchDocumentKeys.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package com.ironcorelabs.tenantsecurity.kms.v1;
22

33
import java.util.Map;
4-
54
import com.google.api.client.util.Key;
65

76
/**
87
* A map from a document ID to a either the wrapped or unwrapped version of a documents keys. Also
98
* includes a map of failures if any problems occurred when performing the batch wrap operation.
109
*/
11-
public class BatchDocumentKeys<T> {
10+
public class BatchDocumentKeys<T> extends NullParsingValidator {
1211
@Key
1312
private Map<String, T> keys;
1413

@@ -22,4 +21,11 @@ public Map<String, T> getKeys() {
2221
public Map<String, ErrorResponse> getFailures() {
2322
return this.failures;
2423
}
24+
25+
@Override
26+
void ensureNoNullsOrThrow() throws IllegalArgumentException {
27+
if (keys == null || failures == null)
28+
throw new IllegalArgumentException(
29+
"Batch response from the Tenant Security Proxy was not valid.");
30+
}
2531
}

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/DeriveKeyResponse.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.google.api.client.util.Key;
66
import com.ironcorelabs.tenantsecurity.kms.v1.exception.TspServiceException;
77

8-
public final class DeriveKeyResponse {
8+
public final class DeriveKeyResponse extends NullParsingValidator {
99
@Key
1010
private boolean hasPrimaryConfig;
1111
@Key
@@ -37,4 +37,10 @@ CompletableFuture<DerivedKey[]> getDerivedKeys(String secretPath, String derivat
3737
}
3838
return CompletableFuture.completedFuture(derivedKeys);
3939
}
40+
41+
@Override
42+
void ensureNoNullsOrThrow() throws IllegalArgumentException {
43+
if (derivedKeys == null)
44+
throw new IllegalArgumentException("TSP failed to derive keys.");
45+
}
4046
}

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/DeterministicCryptoUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ static CompletableFuture<DeterministicPlaintextField> decryptField(
106106
return key;
107107
}).thenCompose(key -> decryptBytes(parts.getEncryptedBytes(), key.getDerivedKeyBytes())))
108108
.thenApply(decrypted -> new DeterministicPlaintextField(decrypted,
109-
encryptedField.getDerivationPath(), encryptedField.getSecretPath()));
109+
encryptedField.getSecretPath(), encryptedField.getDerivationPath()));
110110
}
111111

112112

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.ironcorelabs.tenantsecurity.kms.v1;
2+
3+
abstract class NullParsingValidator {
4+
/**
5+
* Throws an IllegalArgumentException if any of the fields were parsed as null.
6+
*/
7+
abstract void ensureNoNullsOrThrow() throws IllegalArgumentException;
8+
}

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/RekeyedDocumentKey.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@
55
/**
66
* An EDEK made by wrapping an existing encrypted document with a tenant's KMS, in Base64 format.
77
*/
8-
public class RekeyedDocumentKey {
8+
public class RekeyedDocumentKey extends NullParsingValidator {
99
@Key
1010
private String edek;
1111

1212
public String getEdek() {
1313
return this.edek;
1414
}
15+
16+
@Override
17+
void ensureNoNullsOrThrow() throws IllegalArgumentException {
18+
if (edek == null)
19+
throw new IllegalArgumentException(
20+
"Rekeyed document key response from the Tenant Security Proxy was not valid base64.");
21+
}
1522
}

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public Builder allowInsecureHttp(boolean allow) {
189189
}
190190

191191
/**
192-
* Construct the TenantSecurityClient fron the builder.
192+
* Construct the TenantSecurityClient from the builder.
193193
*
194194
* @return The newly constructed TenantSecurityClient.
195195
* @throws Exception If the tsp url isn't valid or if HTTPS is required and not provided.

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/TenantSecurityRequest.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,42 @@ enum SecretType {
153153
* Generic method for making a request to the provided URL with the provided post data. Returns an
154154
* instance of the provided generic JSON class or an error message with the provided error.
155155
*/
156-
private <T> CompletableFuture<T> makeRequestAndParseFailure(GenericUrl url,
157-
Map<String, Object> postData, Class<T> jsonType, String errorMessage) {
156+
private <T extends NullParsingValidator> CompletableFuture<T> makeRequestAndParseFailure(
157+
GenericUrl url, Map<String, Object> postData, Class<T> jsonType, String errorMessage) {
158158
return CompletableFuture.supplyAsync(() -> {
159159
try {
160160
HttpResponse resp = this.getApiRequest(postData, url).execute();
161161
if (resp.isSuccessStatusCode()) {
162-
return resp.parseAs(jsonType);
162+
T parsed = resp.parseAs(jsonType);
163+
parsed.ensureNoNullsOrThrow();
164+
return parsed;
165+
}
166+
throw parseFailureFromRequest(resp);
167+
} catch (Exception cause) {
168+
if (cause instanceof TenantSecurityException) {
169+
throw new CompletionException(cause);
170+
} else if (cause instanceof IllegalArgumentException) {
171+
throw new CompletionException(new TspServiceException(
172+
TenantSecurityErrorCodes.UNKNOWN_ERROR, 0, errorMessage, cause));
173+
}
174+
throw new CompletionException(new TspServiceException(
175+
TenantSecurityErrorCodes.UNABLE_TO_MAKE_REQUEST, 0, errorMessage, cause));
176+
}
177+
}, webRequestExecutor);
178+
}
179+
180+
/**
181+
* Overload for generic method for making a request to the provided URL with the provided post
182+
* data. Returns a CompletableFuture<Void> because it does not try to parse a successful result.
183+
* In the case of an error, it does try to parse the provided error.
184+
*/
185+
private CompletableFuture<Void> makeRequestAndParseFailure(GenericUrl url,
186+
Map<String, Object> postData, String errorMessage) {
187+
return CompletableFuture.supplyAsync(() -> {
188+
try {
189+
HttpResponse resp = this.getApiRequest(postData, url).execute();
190+
if (resp.isSuccessStatusCode()) {
191+
return null;
163192
}
164193
throw parseFailureFromRequest(resp);
165194
} catch (Exception cause) {
@@ -261,7 +290,7 @@ CompletableFuture<Void> logSecurityEvent(SecurityEvent event, EventMetadata meta
261290
String error = String.format(
262291
"Unable to make request to Tenant Security Proxy security event endpoint. Endpoint requested: %s",
263292
this.securityEventEndpoint);
264-
return this.makeRequestAndParseFailure(this.securityEventEndpoint, postData, Void.class, error);
293+
return this.makeRequestAndParseFailure(this.securityEventEndpoint, postData, error);
265294
}
266295

267296
/**

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/UnwrappedDocumentKey.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* Represents the JSON response object from the document/unwrap endpoint which includes the dek.
99
*/
10-
public class UnwrappedDocumentKey {
10+
public class UnwrappedDocumentKey extends NullParsingValidator {
1111
@Key
1212
private String dek;
1313

@@ -19,4 +19,11 @@ public byte[] getDekBytes() {
1919
"Unwrap DEK response from the Tenant Security Proxy was not valid base64.");
2020
}
2121
}
22+
23+
@Override
24+
void ensureNoNullsOrThrow() throws IllegalArgumentException {
25+
if (dek == null)
26+
throw new IllegalArgumentException(
27+
"Unwrap DEK response from the Tenant Security Proxy was not valid base64.");
28+
}
2229
}

src/main/java/com/ironcorelabs/tenantsecurity/kms/v1/WrappedDocumentKey.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* A new DEK wrapped by the tenant's KMS and its encrypted form (EDEK), both in Base64 format.
99
*/
10-
public class WrappedDocumentKey {
10+
public class WrappedDocumentKey extends NullParsingValidator {
1111
@Key
1212
private String dek;
1313

@@ -26,4 +26,11 @@ public byte[] getDekBytes() {
2626
public String getEdek() {
2727
return this.edek;
2828
}
29+
30+
@Override
31+
void ensureNoNullsOrThrow() throws IllegalArgumentException {
32+
if (edek == null || dek == null)
33+
throw new IllegalArgumentException(
34+
"Wrapped document key response from the Tenant Security Proxy was not valid base64.");
35+
}
2936
}

0 commit comments

Comments
 (0)