Skip to content

Commit 76c3a48

Browse files
authored
Fix regression in storage url signing of objects with slashes in names (#1348)
* Fix regression in storage url signing of objects with slashes in names * Add special characters to signUrl integration test
1 parent 201eda8 commit 76c3a48

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,8 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
528528
if (blobInfo.getName().startsWith("/")) {
529529
path.setLength(path.length() - 1);
530530
}
531-
path.append(UrlEscapers.urlPathSegmentEscaper().escape(blobInfo.getName()));
531+
String escapedName = UrlEscapers.urlFragmentEscaper().escape(blobInfo.getName());
532+
path.append(escapedName.replace("?", "%3F"));
532533
stBuilder.append(path);
533534
try {
534535
byte[] signatureBytes = authCredentials.sign(stBuilder.toString().getBytes(UTF_8));

google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,7 @@ public void testSignUrlLeadingSlash() throws NoSuchAlgorithmException, InvalidKe
12591259
storage = options.toBuilder().authCredentials(authCredentials).build().service();
12601260
URL url =
12611261
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
1262-
String escapedBlobName = UrlEscapers.urlPathSegmentEscaper().escape(blobName);
1262+
String escapedBlobName = UrlEscapers.urlFragmentEscaper().escape(blobName);
12631263
String stringUrl = url.toString();
12641264
String expectedUrl = new StringBuilder("https://storage.googleapis.com/").append(BUCKET_NAME1)
12651265
.append(escapedBlobName).append("?GoogleAccessId=").append(ACCOUNT).append("&Expires=")
@@ -1323,7 +1323,8 @@ public void testSignUrlForBlobWithSpecialChars() throws NoSuchAlgorithmException
13231323
String blobName = "/a" + specialChar + "b";
13241324
URL url =
13251325
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
1326-
String escapedBlobName = UrlEscapers.urlPathSegmentEscaper().escape(blobName);
1326+
String escapedBlobName =
1327+
UrlEscapers.urlFragmentEscaper().escape(blobName).replace("?", "%3F");
13271328
String stringUrl = url.toString();
13281329
String expectedUrl = new StringBuilder("https://storage.googleapis.com/").append(BUCKET_NAME1)
13291330
.append(escapedBlobName).append("?GoogleAccessId=").append(ACCOUNT).append("&Expires=")
@@ -1343,6 +1344,36 @@ public void testSignUrlForBlobWithSpecialChars() throws NoSuchAlgorithmException
13431344
}
13441345
}
13451346

1347+
@Test
1348+
public void testSignUrlForBlobWithSlashes() throws NoSuchAlgorithmException,
1349+
InvalidKeyException, SignatureException, UnsupportedEncodingException {
1350+
EasyMock.replay(storageRpcMock);
1351+
ServiceAccountAuthCredentials authCredentials =
1352+
ServiceAccountAuthCredentials.createFor(ACCOUNT, privateKey);
1353+
storage = options.toBuilder().authCredentials(authCredentials).build().service();
1354+
1355+
String blobName = "/foo/bar/baz #%20other cool stuff.txt";
1356+
URL url =
1357+
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
1358+
String escapedBlobName = UrlEscapers.urlFragmentEscaper().escape(blobName);
1359+
String stringUrl = url.toString();
1360+
String expectedUrl = new StringBuilder("https://storage.googleapis.com/").append(BUCKET_NAME1)
1361+
.append(escapedBlobName).append("?GoogleAccessId=").append(ACCOUNT).append("&Expires=")
1362+
.append(42L + 1209600).append("&Signature=").toString();
1363+
assertTrue(stringUrl.startsWith(expectedUrl));
1364+
String signature = stringUrl.substring(expectedUrl.length());
1365+
1366+
StringBuilder signedMessageBuilder = new StringBuilder();
1367+
signedMessageBuilder.append(HttpMethod.GET).append("\n\n\n").append(42L + 1209600)
1368+
.append("\n/").append(BUCKET_NAME1).append(escapedBlobName);
1369+
1370+
Signature signer = Signature.getInstance("SHA256withRSA");
1371+
signer.initVerify(publicKey);
1372+
signer.update(signedMessageBuilder.toString().getBytes(UTF_8));
1373+
assertTrue(signer.verify(BaseEncoding.base64().decode(
1374+
URLDecoder.decode(signature, UTF_8.name()))));
1375+
}
1376+
13461377
@Test
13471378
public void testGetAllArray() {
13481379
BlobId blobId1 = BlobId.of(BUCKET_NAME1, BLOB_NAME1);

google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,7 @@ public void testWriteChannelExistingBlob() throws IOException {
11931193

11941194
@Test
11951195
public void testGetSignedUrl() throws IOException {
1196-
String blobName = "test-get-signed-url-blob";
1196+
String blobName = "test-get-signed-url-blob/with/slashes/and?special=!#$&'()*+,:;=?@[]";
11971197
BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
11981198
Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT);
11991199
assertNotNull(remoteBlob);

0 commit comments

Comments
 (0)