Skip to content

Commit e302cbb

Browse files
committed
Add safelog annotations to SerializableError and RemoteException
1 parent 24487e9 commit e302cbb

File tree

4 files changed

+47
-4
lines changed

4 files changed

+47
-4
lines changed

errors/src/main/java/com/palantir/conjure/java/api/errors/RemoteException.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.palantir.logsafe.Arg;
2020
import com.palantir.logsafe.SafeArg;
2121
import com.palantir.logsafe.SafeLoggable;
22+
import com.palantir.logsafe.Unsafe;
23+
import com.palantir.logsafe.UnsafeArg;
2224
import java.util.Arrays;
2325
import java.util.Collections;
2426
import java.util.List;
@@ -30,12 +32,15 @@ public final class RemoteException extends RuntimeException implements SafeLogga
3032
private static final String ERROR_CODE = "errorCode";
3133
private static final String ERROR_NAME = "errorName";
3234

35+
@Unsafe // because errorName is unsafe
3336
private final String stableMessage;
37+
3438
private final SerializableError error;
3539
private final int status;
3640
private final List<Arg<?>> args;
3741
// Lazily evaluated based on the stableMessage, errorInstanceId, and args.
3842
@SuppressWarnings("MutableException")
43+
@Unsafe
3944
private String unsafeMessage;
4045

4146
/** Returns the error thrown by a remote process which caused an RPC call to fail. */
@@ -56,10 +61,11 @@ public RemoteException(SerializableError error, int status) {
5661
this.status = status;
5762
this.args = Collections.unmodifiableList(Arrays.asList(
5863
SafeArg.of(ERROR_INSTANCE_ID, error.errorInstanceId()),
59-
SafeArg.of(ERROR_NAME, error.errorName()),
64+
UnsafeArg.of(ERROR_NAME, error.errorName()),
6065
SafeArg.of(ERROR_CODE, error.errorCode())));
6166
}
6267

68+
@Unsafe
6369
@Override
6470
public String getMessage() {
6571
// This field is not used in most environments so the cost of computation may be avoided.
@@ -71,6 +77,7 @@ public String getMessage() {
7177
return messageValue;
7278
}
7379

80+
@Unsafe
7481
private String renderUnsafeMessage() {
7582
StringBuilder builder = new StringBuilder()
7683
.append(stableMessage)
@@ -89,6 +96,7 @@ private String renderUnsafeMessage() {
8996
return builder.toString();
9097
}
9198

99+
@Unsafe
92100
@Override
93101
public String getLogMessage() {
94102
return stableMessage;

errors/src/main/java/com/palantir/conjure/java/api/errors/SerializableError.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2222
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2323
import com.palantir.logsafe.Arg;
24+
import com.palantir.logsafe.Safe;
25+
import com.palantir.logsafe.Unsafe;
2426
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
2527
import java.io.Serializable;
2628
import java.util.Map;
@@ -34,6 +36,7 @@
3436
* transport errors through RPC channels such as HTTP responses.
3537
*/
3638
// Automatically suppressed to unblock enforcement in new code
39+
@Unsafe
3740
@SuppressWarnings("ImmutablesStyle")
3841
@JsonDeserialize(builder = SerializableError.Builder.class)
3942
@JsonSerialize(as = ImmutableSerializableError.class)
@@ -48,6 +51,7 @@ public abstract class SerializableError implements Serializable {
4851
* the server-side error code via {@link RemoteException#getError} and typically switch&dispatch on the error code
4952
* and/or name.
5053
*/
54+
@Safe
5155
@JsonProperty("errorCode")
5256
@Value.Default
5357
public String errorCode() {
@@ -61,6 +65,7 @@ public String errorCode() {
6165
* {@link ErrorType#name} and is part of the service's API surface. Clients are given access to the service-side
6266
* error name via {@link RemoteException#getError} and typically switch&dispatch on the error code and/or name.
6367
*/
68+
@Unsafe // because message is unsafe
6469
@JsonProperty("errorName")
6570
@Value.Default
6671
public String errorName() {
@@ -74,6 +79,7 @@ public String errorName() {
7479
* {@link #errorName}, the {@link #errorInstanceId} identifies a specific occurrence of an error, not a class of
7580
* errors. By convention, this field is a UUID.
7681
*/
82+
@Safe
7783
@JsonProperty("errorInstanceId")
7884
@Value.Default
7985
@SuppressWarnings("checkstyle:designforextension")
@@ -89,6 +95,7 @@ public String errorInstanceId() {
8995
*
9096
* @deprecated Used by the serialization-mechanism for back-compat only. Do not use.
9197
*/
98+
@Safe
9299
@Deprecated
93100
@JsonProperty(value = "exceptionClass", access = JsonProperty.Access.WRITE_ONLY)
94101
@Value.Auxiliary
@@ -100,6 +107,7 @@ public String errorInstanceId() {
100107
*
101108
* @deprecated Used by the serialization-mechanism for back-compat only. Do not use.
102109
*/
110+
@Unsafe
103111
@Deprecated
104112
@JsonProperty(value = "message", access = JsonProperty.Access.WRITE_ONLY)
105113
@Value.Auxiliary

errors/src/test/java/com/palantir/conjure/java/api/errors/RemoteExceptionTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020

2121
import com.palantir.logsafe.SafeArg;
22+
import com.palantir.logsafe.UnsafeArg;
2223
import org.apache.commons.lang3.SerializationUtils;
2324
import org.junit.jupiter.api.Test;
2425

@@ -129,7 +130,7 @@ public void testArgsContainsOnlyErrorInstanceId() {
129130
assertThat(remoteException.getArgs())
130131
.containsExactlyInAnyOrder(
131132
SafeArg.of("errorInstanceId", "errorId"),
132-
SafeArg.of("errorCode", "errorCode"),
133+
UnsafeArg.of("errorCode", "errorCode"),
133134
SafeArg.of("errorName", "errorName"));
134135
}
135136
}

errors/src/test/java/com/palantir/conjure/java/api/errors/SerializableErrorTest.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2121

22+
import com.fasterxml.jackson.core.JsonProcessingException;
2223
import com.fasterxml.jackson.databind.ObjectMapper;
2324
import com.palantir.conjure.java.api.ext.jackson.ObjectMappers;
2425
import com.palantir.logsafe.SafeArg;
@@ -146,18 +147,39 @@ public void forException_optionalArgValue_serializesWithToString() {
146147

147148
@Test
148149
public void testSerializationContainsRedundantParameters() throws Exception {
149-
assertThat(mapper.writeValueAsString(ERROR))
150+
assertThat(serialize(ERROR))
150151
.isEqualTo("{\"errorCode\":\"PERMISSION_DENIED\",\"errorName\":\"Product:SomethingBroke\","
151152
+ "\"errorInstanceId\":\"\",\"parameters\":{}}");
152153

153-
assertThat(mapper.writeValueAsString(SerializableError.builder()
154+
assertThat(serialize(SerializableError.builder()
154155
.from(ERROR)
155156
.errorInstanceId("errorId")
156157
.build()))
157158
.isEqualTo("{\"errorCode\":\"PERMISSION_DENIED\",\"errorName\":\"Product:SomethingBroke\","
158159
+ "\"errorInstanceId\":\"errorId\",\"parameters\":{}}");
159160
}
160161

162+
@Test
163+
public void testSerDeRoundTripDropsMessage() throws Exception {
164+
SerializableError error = SerializableError.builder()
165+
.from(ERROR)
166+
.message("the secret is 42")
167+
.build();
168+
169+
assertThat(error.getMessage()).hasValue("the secret is 42");
170+
assertThat(deserialize(serialize(error)).getMessage()).isEmpty();
171+
}
172+
173+
@Test
174+
public void testLegacyMessageUsedAsErrorNameWhenNoErrorNameIsSet() {
175+
SerializableError error = SerializableError.builder()
176+
.errorCode("errorCode")
177+
.message("the secret is 42")
178+
.build();
179+
180+
assertThat(error.errorName()).isEqualTo("the secret is 42");
181+
}
182+
161183
@Test
162184
public void testDeserializesWhenRedundantParamerersAreGiven() throws Exception {
163185
String serialized =
@@ -194,4 +216,8 @@ public void testDeserializationFailsWhenNeitherErrorNameNorMessageIsSet() throws
194216
private static SerializableError deserialize(String serialized) throws IOException {
195217
return mapper.readValue(serialized, SerializableError.class);
196218
}
219+
220+
private static String serialize(SerializableError error) throws JsonProcessingException {
221+
return mapper.writeValueAsString(error);
222+
}
197223
}

0 commit comments

Comments
 (0)