Repository: googleapis/google-http-java-client
Pull request #1940 (2024-05-09 16:34:52)

chore: update dependency versions in java templates (#1964)
* chore: update dependency versions in java templates

* update other templates
Source-Link: https://github.com/googleapis/synthtool/commit/0b86c72fe652dd7e52ba05a63f61bc1399ad5d65
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-java:latest@sha256:68ba5f5164a4b55529d358bb262feaa000536a0c62980727dd05a91bbb47ea5e


Patch:
diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index db1099be..5db36a5f 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
 # limitations under the License.
 docker:
   image: gcr.io/cloud-devrel-public-resources/owlbot-java:latest
-  digest: sha256:084ad4c60551b075846bcb2405ec1c14b0d00ec1eb5503d4dd0d2a92cdc2d3e2
-# created: 2024-03-15T14:33:32.257974519Z
+  digest: sha256:68ba5f5164a4b55529d358bb262feaa000536a0c62980727dd05a91bbb47ea5e
+# created: 2024-05-09T16:31:37.168667071Z
diff --git a/.github/workflows/approve-readme.yaml b/.github/workflows/approve-readme.yaml
index f5fc7d51..59f00b8e 100644
--- a/.github/workflows/approve-readme.yaml
+++ b/.github/workflows/approve-readme.yaml
@@ -21,7 +21,7 @@ jobs:
     runs-on: ubuntu-latest
     if: github.repository_owner == 'googleapis' && github.head_ref == 'autosynth-readme'
     steps:
-      - uses: actions/github-script@v6
+      - uses: actions/github-script@v7
         with:
           github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
           script: |
diff --git a/.github/workflows/renovate_config_check.yaml b/.github/workflows/renovate_config_check.yaml
index 87d8eb2b..7c5ec786 100644
--- a/.github/workflows/renovate_config_check.yaml
+++ b/.github/workflows/renovate_config_check.yaml
@@ -14,7 +14,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: Set up Node.js
-      uses: actions/setup-node@v3
+      uses: actions/setup-node@v4
       with:
         node-version: '20'
 
diff --git a/google-http-client/src/main/java/com/google/api/client/util/Base64.java b/google-http-client/src/main/java/com/google/api/client/util/Base64.java
index 9225cd5d..178bc482 100644
--- a/google-http-client/src/main/java/com/google/api/client/util/Base64.java
+++ b/google-http-client/src/main/java/com/google/api/client/util/Base64.java
@@ -26,6 +26,13 @@ import com.google.common.io.BaseEncoding.DecodingException;
  */
 @Deprecated
 public class Base64 {
+  // Guava's Base64 (https://datatracker.ietf.org/doc/html/rfc4648#section-4) decoders. When
+  // decoding, they discard the new line character so that the behavior matches what we had with
+  // Apache Commons Codec's decodeBase64.
+  // The 2nd argument of the withSeparator method, "64", does not have any effect in decoding.
+  private static final BaseEncoding BASE64_DECODER = BaseEncoding.base64().withSeparator("\n", 64);
+  private static final BaseEncoding BASE64URL_DECODER =
+      BaseEncoding.base64Url().withSeparator("\n", 64);
 
   /**
    * Encodes binary data using the base64 algorithm but does not chunk the output.
@@ -92,6 +99,9 @@ public class Base64 {
    * Decodes a Base64 String into octets. Note that this method handles both URL-safe and
    * non-URL-safe base 64 encoded strings.
    *
+   * <p>For the compatibility with the old version that used Apache Commons Coded's decodeBase64,
+   * this method discards new line characters and trailing whitespaces.
+   *
    * @param base64String String containing Base64 data or {@code null} for {@code null} result
    * @return Array containing decoded data or {@code null} for {@code null} input
    */
@@ -100,10 +110,10 @@ public class Base64 {
       return null;
     }
     try {
-      return BaseEncoding.base64().decode(base64String);
+      return BASE64_DECODER.decode(base64String);
     } catch (IllegalArgumentException e) {
       if (e.getCause() instanceof DecodingException) {
-        return BaseEncoding.base64Url().decode(base64String.trim());
+        return BASE64URL_DECODER.decode(base64String.trim());
       }
       throw e;
     }


Test patch:
diff --git a/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java b/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java
index 0ee174f9..218dd1f1 100644
--- a/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java
+++ b/google-http-client/src/test/java/com/google/api/client/util/Base64Test.java
@@ -14,6 +14,8 @@
 
 package com.google.api.client.util;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import java.nio.charset.StandardCharsets;
 import junit.framework.TestCase;
 
@@ -62,4 +64,73 @@ public class Base64Test extends TestCase {
   public void test_encodeBase64_withNull_shouldReturnNull() {
     assertNull(Base64.encodeBase64(null));
   }
+
+  public void test_decodeBase64_newline_character_invalid_length() {
+    // The RFC 4648 (https://datatracker.ietf.org/doc/html/rfc4648#section-3.3) states that a
+    // specification referring to the Base64 encoding may state that it ignores characters outside
+    // the base alphabet.
+
+    // In Base64 encoding, 3 characters (24 bits) are converted to 4 of 6-bits, each of which is
+    // converted to a byte (a character).
+    // Base64encode("abc") => "YWJj" (4 characters)
+    // Base64encode("def") => "ZGVm" (4 characters)
+    // Adding a new line character between them. This should be discarded.
+    String encodedString = "YWJj\nZGVm";
+
+    // This is a reference implementation by Apache Commons Codec. It discards the new line
+    // characters.
+    // assertEquals(
+    //    "abcdef",
+    //    new String(
+    //        org.apache.commons.codec.binary.Base64.decodeBase64(encodedString),
+    //        StandardCharsets.UTF_8));
+
+    // This is our implementation. Before the
+    // https://github.com/googleapis/google-http-java-client/pull/1941/, it was throwing
+    // IllegalArgumentException("Invalid length 9").
+    assertEquals("abcdef", new String(Base64.decodeBase64(encodedString), StandardCharsets.UTF_8));
+  }
+
+  public void test_decodeBase64_newline_character() {
+    // In Base64 encoding, 2 characters (16 bits) are converted to 3 of 6-bits plus the padding
+    // character ('=").
+    // Base64encode("ab") => "YWI=" (3 characters + padding character)
+    // Adding a new line character that should be discarded between them
+    String encodedString = "YW\nI=";
+
+    // This is a reference implementation by Apache Commons Codec. It discards the new line
+    // characters.
+    // assertEquals(
+    //    "ab",
+    //    new String(
+    //        org.apache.commons.codec.binary.Base64.decodeBase64(encodedString),
+    //        StandardCharsets.UTF_8));
+
+    // This is our implementation. Before the fix
+    // https://github.com/googleapis/google-http-java-client/pull/1941/, it was throwing
+    // IllegalArgumentException("Unrecognized character: 0xa").
+    assertEquals("ab", new String(Base64.decodeBase64(encodedString), StandardCharsets.UTF_8));
+  }
+
+  public void test_decodeBase64_plus_and_newline_characters() {
+    // The plus sign is 62 in the Base64 table. So it's a valid character in encoded strings.
+    // https://datatracker.ietf.org/doc/html/rfc4648#section-4
+    String encodedString = "+\nw==";
+
+    byte[] actual = Base64.decodeBase64(encodedString);
+    // Before the fix https://github.com/googleapis/google-http-java-client/pull/1941/, it was
+    // throwing IllegalArgumentException("Unrecognized character: +").
+    assertThat(actual).isEqualTo(new byte[] {(byte) 0xfb});
+  }
+
+  public void test_decodeBase64_slash_and_newline_characters() {
+    // The slash sign is 63 in the Base64 table. So it's a valid character in encoded strings.
+    // https://datatracker.ietf.org/doc/html/rfc4648#section-4
+    String encodedString = "/\nw==";
+
+    byte[] actual = Base64.decodeBase64(encodedString);
+    // Before the fix https://github.com/googleapis/google-http-java-client/pull/1941/, it was
+    // throwing IllegalArgumentException("Unrecognized character: /").
+    assertThat(actual).isEqualTo(new byte[] {(byte) 0xff});
+  }
 }