Skip to content

Commit

Permalink
fix: remove sensitive data when BoxAPIException logs request (#1284)
Browse files Browse the repository at this point in the history
* fix: remove sensitive data when BoxAPIException logs request

* remove wiremock setup

* checkstyle

* checkstyle test

* allow customizing sanitization keys
  • Loading branch information
mwwoda authored Jan 22, 2025
1 parent ed82a13 commit f1e226f
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/main/java/com/box/sdk/BoxAPIConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import okhttp3.Authenticator;
import okhttp3.Call;
import okhttp3.Credentials;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
Expand Down Expand Up @@ -1265,7 +1266,8 @@ private Response executeOnClient(OkHttpClient httpClient, Request request) {
try {
return createNewCall(httpClient, request).execute();
} catch (IOException e) {
throw new BoxAPIException("Couldn't connect to the Box API due to a network error. Request\n" + request, e);
throw new BoxAPIException("Couldn't connect to the Box API due to a network error. Request\n"
+ toSanitizedRequest(request), e);
}
}

Expand Down Expand Up @@ -1298,4 +1300,12 @@ protected enum ResourceLinkType {
*/
SharedLink
}

private Request toSanitizedRequest(Request originalRequest) {
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(originalRequest.headers());

return originalRequest.newBuilder()
.headers(sanitizedHeaders)
.build();
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/box/sdk/BoxSensitiveDataSanitizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.box.sdk;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import okhttp3.Headers;
import org.jetbrains.annotations.NotNull;

/**
* Class used to sanitize sensitive data from payload.
*/
public final class BoxSensitiveDataSanitizer {
private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("authorization", "access_token",
"refresh_token", "subject_token", "token", "client_id", "client_secret", "code", "shared_link", "download_url",
"jwt_private_key", "jwt_private_key_passphrase", "password"));

private BoxSensitiveDataSanitizer() {
}

/**
* Add key that should be sanitized
*
* @param key key to be sanitized
*/
public static void addKeyToSanitize(String key) {
SENSITIVE_KEYS.add(key);
}

@NotNull
static Headers sanitizeHeaders(Headers originalHeaders) {
Headers.Builder sanitizedHeadersBuilder = originalHeaders.newBuilder();

for (String originalHeaderName : originalHeaders.names()) {
if (isSensitiveKey(originalHeaderName)) {
sanitizedHeadersBuilder.set(originalHeaderName, "[REDACTED]");
} else {
String headerValue = originalHeaders.get(originalHeaderName);
if (headerValue != null) {
sanitizedHeadersBuilder.set(originalHeaderName, headerValue);
}
}
}

return sanitizedHeadersBuilder.build();
}

private static boolean isSensitiveKey(@NotNull String key) {
return SENSITIVE_KEYS.contains(key.toLowerCase());
}
}
88 changes: 88 additions & 0 deletions src/test/java/com/box/sdk/BoxSensitiveDataSanitizerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.box.sdk;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

import java.util.HashMap;
import java.util.Map;
import okhttp3.Headers;
import org.junit.Test;

public class BoxSensitiveDataSanitizerTest {
@Test
public void removeSensitiveDataFromHeaders() {
Map<String, String> headersMap = new HashMap<>();
headersMap.put("authorization", "token");
headersMap.put("user-agent", "java-sdk");

Headers headers = Headers.of(headersMap);
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(2));
assertThat(sanitizedHeaders.get("authorization"), is("[REDACTED]"));
assertThat(sanitizedHeaders.get("user-agent"), is("java-sdk"));
}

@Test
public void removeAllHeadersWhenOnlySensitiveData() {
Map<String, String> headersMap = new HashMap<>();
headersMap.put("authorization", "token");
headersMap.put("password", "123");

Headers headers = Headers.of(headersMap);
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(2));
assertThat(sanitizedHeaders.get("authorization"), is("[REDACTED]"));
assertThat(sanitizedHeaders.get("password"), is("[REDACTED]"));
}

@Test
public void removeSensitiveDataFromHeadersWhenUppercase() {
Map<String, String> headersMap = new HashMap<>();
headersMap.put("Authorization", "token");
headersMap.put("user-agent", "java-sdk");

Headers headers = Headers.of(headersMap);
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(2));
assertThat(sanitizedHeaders.get("Authorization"), is("[REDACTED]"));
assertThat(sanitizedHeaders.get("user-agent"), is("java-sdk"));
}

@Test
public void headersNotRemovedWhenNoSensitiveData() {
Map<String, String> headersMap = new HashMap<>();
headersMap.put("accept", "application/json");
headersMap.put("user-agent", "java-sdk");

Headers headers = Headers.of(headersMap);
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(2));
assertThat(sanitizedHeaders.get("accept"), is("application/json"));
assertThat(sanitizedHeaders.get("user-agent"), is("java-sdk"));
}

@Test
public void returnEmptyHeadersWhenEmptyHeadersPassed() {
Headers headers = Headers.of(new HashMap<>());
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(0));
}

@Test
public void sanitizeAddedKeys() {
Map<String, String> headersMap = new HashMap<>();
headersMap.put("x-auth", "token");

Headers headers = Headers.of(headersMap);
BoxSensitiveDataSanitizer.addKeyToSanitize("x-auth");
Headers sanitizedHeaders = BoxSensitiveDataSanitizer.sanitizeHeaders(headers);

assertThat(sanitizedHeaders.size(), is(1));
assertThat(sanitizedHeaders.get("x-auth"), is("[REDACTED]"));
}
}

0 comments on commit f1e226f

Please sign in to comment.