Skip to content

Commit 8bcb2de

Browse files
authored
fix: update usages of String.format to explicitly pass Locale.US (#2974)
Usage of `Storage#format(String, Object...)` will use the default locale for it's formatting. This can lead to unexpected formatting if a right-to-left language such as Arabic is the default locale. Update all usages to use `String.format(Locale.US, pattern, Object...)` so we ensure things like headers or error messages are formatted according to the US conventions which match `en_US` and ascii byte conventions. Incidentally, things like right-to-left formatting seem to only apply to java11+ not java8. Fixes #2972
1 parent d092c9b commit 8bcb2de

File tree

76 files changed

+477
-175
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+477
-175
lines changed

‎google-cloud-storage/src/main/java/com/google/cloud/storage/ApiaryUnbufferedReadableByteChannel.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.nio.channels.ReadableByteChannel;
5353
import java.nio.channels.ScatteringByteChannel;
5454
import java.util.List;
55+
import java.util.Locale;
5556
import java.util.Map;
5657
import java.util.Map.Entry;
5758
import java.util.function.Function;
@@ -318,7 +319,10 @@ private static String getHeaderValue(@NonNull HttpHeaders headers, @NonNull Stri
318319
} else {
319320
throw new IllegalStateException(
320321
String.format(
321-
"Unexpected header type '%s' for header %s", o.getClass().getName(), headerName));
322+
Locale.US,
323+
"Unexpected header type '%s' for header %s",
324+
o.getClass().getName(),
325+
headerName));
322326
}
323327
}
324328

‎google-cloud-storage/src/main/java/com/google/cloud/storage/BlobInfo.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.Collections;
4141
import java.util.HashMap;
4242
import java.util.List;
43+
import java.util.Locale;
4344
import java.util.Map;
4445
import java.util.Objects;
4546
import java.util.Set;
@@ -1441,7 +1442,7 @@ public String getMd5ToHexString() {
14411442
byte[] decodedMd5 = BaseEncoding.base64().decode(md5);
14421443
StringBuilder stringBuilder = new StringBuilder();
14431444
for (byte b : decodedMd5) {
1444-
stringBuilder.append(String.format("%02x", b & 0xff));
1445+
stringBuilder.append(String.format(Locale.US, "%02x", b & 0xff));
14451446
}
14461447
return stringBuilder.toString();
14471448
}
@@ -1473,7 +1474,7 @@ public String getCrc32cToHexString() {
14731474
byte[] decodeCrc32c = BaseEncoding.base64().decode(crc32c);
14741475
StringBuilder stringBuilder = new StringBuilder();
14751476
for (byte b : decodeCrc32c) {
1476-
stringBuilder.append(String.format("%02x", b & 0xff));
1477+
stringBuilder.append(String.format(Locale.US, "%02x", b & 0xff));
14771478
}
14781479
return stringBuilder.toString();
14791480
}

‎google-cloud-storage/src/main/java/com/google/cloud/storage/ByteRangeSpec.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.common.base.MoreObjects.ToStringHelper;
2525
import com.google.storage.v2.ReadObjectRequest;
2626
import java.io.Serializable;
27+
import java.util.Locale;
2728
import java.util.Objects;
2829
import java.util.function.BiFunction;
2930
import javax.annotation.concurrent.Immutable;
@@ -239,12 +240,12 @@ public ReadObjectRequest.Builder seekReadObjectRequest(ReadObjectRequest.Builder
239240

240241
@Override
241242
protected String fmtAsHttpRangeHeader() throws ArithmeticException {
242-
return String.format("bytes=%d-%d", beginOffset, endOffsetInclusive());
243+
return String.format(Locale.US, "bytes=%d-%d", beginOffset, endOffsetInclusive());
243244
}
244245

245246
@Override
246247
protected ToStringHelper append(ToStringHelper tsh) {
247-
return tsh.addValue(String.format("%d + %d", beginOffset, length));
248+
return tsh.addValue(String.format(Locale.US, "%d + %d", beginOffset, length));
248249
}
249250
}
250251

@@ -324,12 +325,12 @@ public ReadObjectRequest.Builder seekReadObjectRequest(ReadObjectRequest.Builder
324325

325326
@Override
326327
protected String fmtAsHttpRangeHeader() throws ArithmeticException {
327-
return String.format("bytes=%d-%d", beginOffset, endOffsetInclusive());
328+
return String.format(Locale.US, "bytes=%d-%d", beginOffset, endOffsetInclusive());
328329
}
329330

330331
@Override
331332
protected ToStringHelper append(ToStringHelper tsh) {
332-
return tsh.addValue(String.format("[%d, %d)", beginOffset, endOffsetExclusive));
333+
return tsh.addValue(String.format(Locale.US, "[%d, %d)", beginOffset, endOffsetExclusive));
333334
}
334335
}
335336

@@ -409,12 +410,12 @@ public ReadObjectRequest.Builder seekReadObjectRequest(ReadObjectRequest.Builder
409410

410411
@Override
411412
protected String fmtAsHttpRangeHeader() throws ArithmeticException {
412-
return String.format("bytes=%d-%d", beginOffset, endOffsetInclusive);
413+
return String.format(Locale.US, "bytes=%d-%d", beginOffset, endOffsetInclusive);
413414
}
414415

415416
@Override
416417
protected ToStringHelper append(ToStringHelper tsh) {
417-
return tsh.addValue(String.format("[%d, %d]", beginOffset, endOffsetInclusive));
418+
return tsh.addValue(String.format(Locale.US, "[%d, %d]", beginOffset, endOffsetInclusive));
418419
}
419420
}
420421

@@ -488,17 +489,17 @@ public ReadObjectRequest.Builder seekReadObjectRequest(ReadObjectRequest.Builder
488489
@Override
489490
protected String fmtAsHttpRangeHeader() throws ArithmeticException {
490491
if (beginOffset > 0) {
491-
return String.format("bytes=%d-", beginOffset);
492+
return String.format(Locale.US, "bytes=%d-", beginOffset);
492493
} else if (beginOffset < 0) {
493-
return String.format("bytes=%d", beginOffset);
494+
return String.format(Locale.US, "bytes=%d", beginOffset);
494495
} else {
495496
return null;
496497
}
497498
}
498499

499500
@Override
500501
protected ToStringHelper append(ToStringHelper tsh) {
501-
return tsh.addValue(String.format("[%d, +INF)", beginOffset));
502+
return tsh.addValue(String.format(Locale.US, "[%d, +INF)", beginOffset));
502503
}
503504
}
504505

‎google-cloud-storage/src/main/java/com/google/cloud/storage/Crc32cValue.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.cloud.storage;
1818

19+
import java.util.Locale;
1920
import java.util.Objects;
2021

2122
abstract class Crc32cValue<Res extends Crc32cValue<Res>> {
@@ -64,7 +65,7 @@ static Crc32cLengthKnown of(int value, long length) {
6465
}
6566

6667
static String fmtCrc32cValue(int value1) {
67-
return String.format("crc32c{0x%08x}", value1);
68+
return String.format(Locale.US, "crc32c{0x%08x}", value1);
6869
}
6970

7071
static final class Crc32cLengthUnknown extends Crc32cValue<Crc32cLengthUnknown> {
@@ -142,7 +143,7 @@ public Crc32cLengthKnown concat(Crc32cLengthKnown other) {
142143

143144
@Override
144145
public String toString() {
145-
return String.format("crc32c{0x%08x (length = %d)}", value, length);
146+
return String.format(Locale.US, "crc32c{0x%08x (length = %d)}", value, length);
146147
}
147148

148149
@Override

‎google-cloud-storage/src/main/java/com/google/cloud/storage/CrossTransportUtils.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.cloud.storage.TransportCompatibility.Transport;
2020
import java.util.Arrays;
21+
import java.util.Locale;
2122
import java.util.stream.Collectors;
2223

2324
final class CrossTransportUtils {
@@ -49,12 +50,20 @@ static <T> T throwTransportOnly(Class<?> clazz, String methodName, Transport tra
4950
break;
5051
default:
5152
throw new IllegalStateException(
52-
String.format("Broken Java Enum: %s received value: '%s'", Transport.class, transport));
53+
String.format(
54+
Locale.US,
55+
"Broken Java Enum: %s received value: '%s'",
56+
Transport.class,
57+
transport));
5358
}
5459
String message =
5560
String.format(
61+
Locale.US,
5662
"%s#%s is only supported for %s transport. Please use %s to construct a compatible instance.",
57-
clazz.getName(), methodName, transport, builder);
63+
clazz.getName(),
64+
methodName,
65+
transport,
66+
builder);
5867
throw new UnsupportedOperationException(message);
5968
}
6069

‎google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedReadableByteChannel.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.nio.ByteBuffer;
4343
import java.nio.channels.ClosedChannelException;
4444
import java.nio.channels.ScatteringByteChannel;
45+
import java.util.Locale;
4546
import java.util.concurrent.ArrayBlockingQueue;
4647
import java.util.concurrent.CancellationException;
4748
import java.util.concurrent.ExecutionException;
@@ -166,8 +167,10 @@ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
166167
} else if (metadata.getGeneration() != respMetadata.getGeneration()) {
167168
throw closeWithError(
168169
String.format(
170+
Locale.US,
169171
"Mismatch Generation between subsequent reads. Expected %d but received %d",
170-
metadata.getGeneration(), respMetadata.getGeneration()));
172+
metadata.getGeneration(),
173+
respMetadata.getGeneration()));
171174
}
172175
}
173176
ChecksummedData checksummedData = resp.getChecksummedData();

‎google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
import java.util.IdentityHashMap;
9797
import java.util.Iterator;
9898
import java.util.List;
99+
import java.util.Locale;
99100
import java.util.Map;
100101
import java.util.Map.Entry;
101102
import java.util.Objects;
@@ -210,10 +211,10 @@ private Tuple<StorageSettings, Opts<UserProject>> resolveSettingsAndOpts() throw
210211
// unless for Direct Google Access try and strip here if we can
211212
switch (scheme) {
212213
case "http":
213-
endpoint = String.format("%s:%s", uri.getHost(), port > 0 ? port : 80);
214+
endpoint = String.format(Locale.US, "%s:%s", uri.getHost(), port > 0 ? port : 80);
214215
break;
215216
case "https":
216-
endpoint = String.format("%s:%s", uri.getHost(), port > 0 ? port : 443);
217+
endpoint = String.format(Locale.US, "%s:%s", uri.getHost(), port > 0 ? port : 443);
217218
break;
218219
}
219220

‎google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcUtils.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.api.gax.grpc.GrpcCallContext;
2020
import com.google.common.collect.ImmutableList;
2121
import com.google.common.collect.ImmutableMap;
22+
import java.util.Locale;
2223

2324
final class GrpcUtils {
2425

@@ -28,7 +29,8 @@ static GrpcCallContext contextWithBucketName(String bucketName, GrpcCallContext
2829
if (bucketName != null && !bucketName.isEmpty()) {
2930
return baseContext.withExtraHeaders(
3031
ImmutableMap.of(
31-
"x-goog-request-params", ImmutableList.of(String.format("bucket=%s", bucketName))));
32+
"x-goog-request-params",
33+
ImmutableList.of(String.format(Locale.US, "bucket=%s", bucketName))));
3234
}
3335
return baseContext;
3436
}

‎google-cloud-storage/src/main/java/com/google/cloud/storage/Hasher.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.nio.ByteBuffer;
2323
import java.util.List;
24+
import java.util.Locale;
2425
import java.util.function.Supplier;
2526
import javax.annotation.concurrent.Immutable;
2627
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -98,8 +99,10 @@ public void validate(Crc32cValue<?> expected, List<ByteBuffer> b) throws IOExcep
9899
if (!actual.eqValue(expected)) {
99100
throw new IOException(
100101
String.format(
102+
Locale.US,
101103
"Mismatch checksum value. Expected %s actual %s",
102-
expected.debugString(), actual.debugString()));
104+
expected.debugString(),
105+
actual.debugString()));
103106
}
104107
}
105108

@@ -109,8 +112,10 @@ public void validate(Crc32cValue<?> expected, Supplier<ByteBuffer> b) throws IOE
109112
if (!actual.eqValue(expected)) {
110113
throw new IOException(
111114
String.format(
115+
Locale.US,
112116
"Mismatch checksum value. Expected %s actual %s",
113-
expected.debugString(), actual.debugString()));
117+
expected.debugString(),
118+
actual.debugString()));
114119
}
115120
}
116121

‎google-cloud-storage/src/main/java/com/google/cloud/storage/HttpContentRange.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.common.base.Preconditions.checkArgument;
2020

2121
import com.google.common.base.MoreObjects;
22+
import java.util.Locale;
2223
import java.util.Objects;
2324
import java.util.function.UnaryOperator;
2425

@@ -91,7 +92,8 @@ private Incomplete(ByteRangeSpec spec) {
9192

9293
@Override
9394
public String getHeaderValue() {
94-
return String.format("bytes %d-%d/*", spec.beginOffset(), spec.endOffsetInclusive());
95+
return String.format(
96+
Locale.US, "bytes %d-%d/*", spec.beginOffset(), spec.endOffsetInclusive());
9597
}
9698

9799
@Override
@@ -145,7 +147,8 @@ private Total(ByteRangeSpec spec, long size) {
145147

146148
@Override
147149
public String getHeaderValue() {
148-
return String.format("bytes %d-%d/%d", spec.beginOffset(), spec.endOffsetInclusive(), size);
150+
return String.format(
151+
Locale.US, "bytes %d-%d/%d", spec.beginOffset(), spec.endOffsetInclusive(), size);
149152
}
150153

151154
@Override
@@ -202,7 +205,7 @@ private Size(long size) {
202205

203206
@Override
204207
public String getHeaderValue() {
205-
return String.format("bytes */%d", size);
208+
return String.format(Locale.US, "bytes */%d", size);
206209
}
207210

208211
@Override

‎google-cloud-storage/src/main/java/com/google/cloud/storage/JsonResumableSession.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
import com.google.cloud.storage.spi.v1.HttpRpcContext;
2424
import com.google.cloud.storage.spi.v1.HttpStorageRpc;
2525
import io.opencensus.trace.EndSpanOptions;
26+
import java.util.Locale;
2627
import java.util.concurrent.atomic.AtomicBoolean;
2728
import org.checkerframework.checker.nullness.qual.Nullable;
2829

2930
final class JsonResumableSession {
3031

3132
static final String SPAN_NAME_WRITE =
32-
String.format("Sent.%s.write", HttpStorageRpc.class.getName());
33+
String.format(Locale.US, "Sent.%s.write", HttpStorageRpc.class.getName());
3334
static final EndSpanOptions END_SPAN_OPTIONS =
3435
EndSpanOptions.builder().setSampleToLocalSpanStore(true).build();
3536

‎google-cloud-storage/src/main/java/com/google/cloud/storage/MetadataField.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.common.base.MoreObjects;
2222
import com.google.common.collect.ImmutableMap;
2323
import java.util.Comparator;
24+
import java.util.Locale;
2425
import java.util.Map;
2526
import java.util.Objects;
2627
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -102,7 +103,7 @@ public long getEnd() {
102103
}
103104

104105
String encode() {
105-
return String.format("%04d-%04d", begin, end);
106+
return String.format(Locale.US, "%04d-%04d", begin, end);
106107
}
107108

108109
static PartRange decode(String s) {

‎google-cloud-storage/src/main/java/com/google/cloud/storage/OtelStorageDecorator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ public Blob get(String bucket, String blob, BlobGetOption... options) {
278278
Span span =
279279
tracer
280280
.spanBuilder("get")
281-
.setAttribute("gsutil.uri", String.format("gs://%s/%s", bucket, blob))
281+
.setAttribute("gsutil.uri", String.format(Locale.US, "gs://%s/%s", bucket, blob))
282282
.startSpan();
283283
try (Scope ignore = span.makeCurrent()) {
284284
return delegate.get(bucket, blob, options);
@@ -1477,7 +1477,7 @@ static Storage decorate(Storage delegate, OpenTelemetry otel, Transport transpor
14771477
}
14781478

14791479
private static @NonNull String fmtBucket(String bucket) {
1480-
return String.format("gs://%s/", bucket);
1480+
return String.format(Locale.US, "gs://%s/", bucket);
14811481
}
14821482

14831483
private static final class TracerDecorator implements Tracer {

‎google-cloud-storage/src/main/java/com/google/cloud/storage/ParallelCompositeUploadWritableByteChannel.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import java.util.Collections;
6464
import java.util.Comparator;
6565
import java.util.List;
66+
import java.util.Locale;
6667
import java.util.Map;
6768
import java.util.NoSuchElementException;
6869
import java.util.Objects;
@@ -245,8 +246,10 @@ public void close() throws IOException {
245246
buildParallelCompositeUploadException(
246247
ApiExceptionFactory.createException(
247248
String.format(
249+
Locale.US,
248250
"CRC32C Checksum mismatch. expected: [%s] but was: [%s]",
249-
expectedCrc32c, crc32c),
251+
expectedCrc32c,
252+
crc32c),
250253
null,
251254
GrpcStatusCode.of(Code.DATA_LOSS),
252255
false),
@@ -477,6 +480,7 @@ private <R> ApiFuture<R> asyncCleanupAfterFailure(Throwable originalFailure) {
477480

478481
String message =
479482
String.format(
483+
Locale.US,
480484
"Incomplete parallel composite upload cleanup after previous error. Unknown object ids: %s",
481485
failedGsUris);
482486
StorageException storageException = new StorageException(0, message, null);

0 commit comments

Comments
 (0)