You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2026/06/04 16:04:18 UTC

(cxf) branch 4.1.x-fixes updated (e8479c34201 -> eb6aa446203)

This is an automated email from the ASF dual-hosted git repository.

reta pushed a change to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


    from e8479c34201 compare username token password and digest in constant time (#3182)
     new 77da0824ccf Bump org.sonarsource.scanner.maven:sonar-maven-plugin (#3178)
     new d3e0353f6e9 Bump org.webjars:swagger-ui from 5.32.5 to 5.32.6 (#3179)
     new 834f1ac94af Bump org.glassfish.jaxb:jaxb-xjc from 4.0.8 to 4.0.9 (#3181)
     new 33c92bd8d09 Introduce default value for maxFormParameterCount (#3177)
     new eb6aa446203 Recording .gitmergeinfo Changes

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitmergeinfo                                      |   2 +
 distribution/src/main/release/samples/pom.xml      |   2 +-
 parent/pom.xml                                     |   4 +-
 pom.xml                                            |   2 +-
 .../java/org/apache/cxf/jaxrs/utils/FormUtils.java |   9 +-
 .../org/apache/cxf/jaxrs/utils/FormUtilsTest.java  | 137 ++++++++++++++++++---
 6 files changed, 128 insertions(+), 28 deletions(-)


(cxf) 04/05: Introduce default value for maxFormParameterCount (#3177)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 33c92bd8d09f5e30b7e56ed7ce746f377323635d
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Thu Jun 4 11:40:57 2026 -0400

    Introduce default value for maxFormParameterCount (#3177)
    
    (cherry picked from commit 1a16a0a28799a119472f0f54c297dc9799c32b5a)
---
 .../java/org/apache/cxf/jaxrs/utils/FormUtils.java |   9 +-
 .../org/apache/cxf/jaxrs/utils/FormUtilsTest.java  | 137 ++++++++++++++++++---
 2 files changed, 122 insertions(+), 24 deletions(-)

diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
index f8443785baa..44f3a6d0b7e 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java
@@ -54,6 +54,8 @@ import org.apache.cxf.phase.PhaseInterceptorChain;
 import org.apache.cxf.transport.http.AbstractHTTPDestination;
 
 public final class FormUtils {
+    public static final int DEFAULT_MAX_FORM_PARAM_COUNT = 500;
+
     public static final String FORM_PARAMS_FROM_HTTP_PARAMS = "set.form.parameters.from.http.parameters";
     public static final String FORM_PARAM_MAP = "org.apache.cxf.form_data";
     public static final String FORM_PARAM_MAP_DECODED = "org.apache.cxf.form_data.decoded";
@@ -290,11 +292,8 @@ public final class FormUtils {
         if (m == null || m.getExchange() == null || m.getExchange().getInMessage() == null) {
             return;
         }
-        String maxPartsCountProp = (String)m.getExchange()
-            .getInMessage().getContextualProperty(MAX_FORM_PARAM_COUNT);
-        if (maxPartsCountProp == null) {
-            return;
-        }
+        final String maxPartsCountProp = MessageUtils.getContextualString(m.getExchange().getInMessage(),
+            MAX_FORM_PARAM_COUNT, Integer.toString(DEFAULT_MAX_FORM_PARAM_COUNT));
         try {
             int maxPartsCount = Integer.parseInt(maxPartsCountProp);
             if (maxPartsCount != -1 && numberOfParts >= maxPartsCount) {
diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java
index 61a5844ce22..fedde8d08fd 100644
--- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java
+++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java
@@ -19,40 +19,59 @@
 
 package org.apache.cxf.jaxrs.utils;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import jakarta.servlet.http.HttpServletRequest;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedMap;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
 import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.message.ExchangeImpl;
 import org.apache.cxf.message.Message;
 
 import org.junit.Test;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 public class FormUtilsTest {
-
-    private static final String HTTP_PARAM1 = "httpParam1";
-    private static final String HTTP_PARAM2 = "httpParam2";
-    private static final String HTTP_PARAM_VALUE1 = "httpValue1";
-    private static final String HTTP_PARAM_VALUE2 = "httpValue2";
-
-    private static final String FORM_PARAM1 = "formParam1";
-    private static final String FORM_PARAM2 = "formParam2";
-    private static final String FORM_PARAM_VALUE1 = "formValue1";
-    private static final String FORM_PARAM_VALUE2 = "formValue2";
+    private static final String HTTP_PARAM = "httpParam";
+    private static final String HTTP_PARAM1 = HTTP_PARAM + "1";
+    private static final String HTTP_PARAM2 = HTTP_PARAM + "2";
+    private static final String HTTP_PARAM_VALUE = "httpValue";
+    private static final String HTTP_PARAM_VALUE1 = HTTP_PARAM_VALUE + "1";
+    private static final String HTTP_PARAM_VALUE2 = HTTP_PARAM_VALUE + "2";
+
+    private static final String FORM_PARAM = "formParam";
+    private static final String FORM_PARAM1 = FORM_PARAM + "1";
+    private static final String FORM_PARAM2 = FORM_PARAM + "2";
+    private static final String FORM_PARAM_VALUE = "formValue";
+    private static final String FORM_PARAM_VALUE1 = FORM_PARAM_VALUE + "1";
+    private static final String FORM_PARAM_VALUE2 = FORM_PARAM_VALUE + "2";
 
     private Message mockMessage;
     private HttpServletRequest mockRequest;
 
     @Test
     public void populateMapFromStringFromHTTP() {
-        mockObjects(null);
+        mockObjects(null, 2);
 
         MultivaluedMap<String, String> params = new MetadataMap<>();
         FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(),
@@ -65,7 +84,7 @@ public class FormUtilsTest {
 
     @Test
     public void populateMapFromStringFromHTTPWithProp() {
-        mockObjects("false");
+        mockObjects("false", 2);
 
         MultivaluedMap<String, String> params = new MetadataMap<>();
         FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(),
@@ -76,7 +95,7 @@ public class FormUtilsTest {
 
     @Test
     public void populateMapFromStringFromBody() {
-        mockObjects(null);
+        mockObjects(null, 2);
 
         MultivaluedMap<String, String> params = new MetadataMap<>();
         String postBody = FORM_PARAM1 + "=" + FORM_PARAM_VALUE1 + "&" + FORM_PARAM2 + "=" + FORM_PARAM_VALUE2;
@@ -89,19 +108,99 @@ public class FormUtilsTest {
     }
 
 
-    private void mockObjects(String formPropertyValue) {
+    @Test
+    public void populateMapFromBodyExceedsDefaultMaxFormParams() {
+        mockObjects(null, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT);
+        
+        final MultivaluedMap<String, String> params = new MetadataMap<>();
+        final String postBody = IntStream
+                .range(1, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1)
+                .mapToObj(i -> FORM_PARAM + i + "=" + FORM_PARAM_VALUE + i)
+                .collect(Collectors.joining("&"));
+        final WebApplicationException ex = assertThrows(WebApplicationException.class,
+            () -> FormUtils.populateMapFromString(params, mockMessage, postBody, 
+                StandardCharsets.UTF_8.name(), false));
+        assertThat(ex.getResponse().getStatus(), equalTo(413)); /* Request Entity Too Large */
+
+        // Increase the limit and try again
+        when(mockMessage.getContextualProperty("maxFormParameterCount"))
+            .thenReturn(Integer.toString(FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1));
+        FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), false, mockRequest);
+        assertEquals(500, params.size());
+    }
+    
+    @Test
+    public void populateMapFromMultiPartExceedsDefaultMaxFormParams() {
+        mockObjects(null, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT);
+
+        final MultivaluedMap<String, String> params = new MetadataMap<>();
+        final MultipartBody body = new MultipartBody(IntStream
+                .range(1, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1)
+                .mapToObj(i -> {
+                    final MultivaluedMap<String, String> headers = new MetadataMap<>();
+                    headers.putSingle("Content-ID", UUID.randomUUID().toString());
+                    return new Attachment(InputStream.nullInputStream(), headers);
+                })
+                .toList());
+        final WebApplicationException ex = assertThrows(WebApplicationException.class,
+            () -> FormUtils.populateMapFromMultipart(params, body, mockMessage, false));
+        assertThat(ex.getResponse().getStatus(), equalTo(413)); /* Request Entity Too Large */
+
+        // Increase the limit and try again
+        when(mockMessage.getContextualProperty("maxFormParameterCount"))
+            .thenReturn(Integer.toString(FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1));
+        FormUtils.populateMapFromMultipart(params, body, mockMessage, false);
+        assertEquals(500, params.size());
+    }
+
+    @Test
+    public void populateMapFromEntrityPartExceedsDefaultMaxFormParams() {
+        mockObjects(null, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT);
+
+        final MultivaluedMap<String, String> params = new MetadataMap<>();
+        final List<EntityPart> body = IntStream
+                .range(1, FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1)
+                .mapToObj(i -> {
+                    try {
+                        return EntityPart.withName(UUID.randomUUID().toString())
+                            .mediaType(MediaType.APPLICATION_OCTET_STREAM)
+                            .content(InputStream.nullInputStream())
+                            .build();
+                    } catch (final IOException ex) {
+                        throw new UncheckedIOException(ex);
+                    }
+                })
+                .toList();
+        final WebApplicationException ex = assertThrows(WebApplicationException.class,
+            () -> FormUtils.populateMapFromEntityParts(params, body, mockMessage, false));
+        assertThat(ex.getResponse().getStatus(), equalTo(413)); /* Request Entity Too Large */
+
+        // Increase the limit and try again
+        when(mockMessage.getContextualProperty("maxFormParameterCount"))
+            .thenReturn(Integer.toString(FormUtils.DEFAULT_MAX_FORM_PARAM_COUNT + 1));
+        FormUtils.populateMapFromEntityParts(params, body, mockMessage, false);
+        assertEquals(500, params.size());
+    }
+    
+    private void mockObjects(String formPropertyValue, int params) {
+        final ExchangeImpl exchange = new ExchangeImpl();
+
         mockMessage = mock(Message.class);
         when(mockMessage.getContextualProperty(FormUtils.FORM_PARAMS_FROM_HTTP_PARAMS))
             .thenReturn(formPropertyValue);
-        when(mockMessage.getExchange()).thenReturn(null);
+        when(mockMessage.getExchange()).thenReturn(exchange);
         when(mockMessage.put(FormUtils.FORM_PARAM_MAP_DECODED, true))
             .thenReturn(null);
-        
+        exchange.setInMessage(mockMessage);
+
         mockRequest = mock(HttpServletRequest.class);
-        String[] httpParamNames = {HTTP_PARAM1, HTTP_PARAM2};
+        String[] httpParamNames =  IntStream.range(1, params + 1)
+            .mapToObj(i -> "httpParam" + i)
+            .toArray(String[]::new);
         Enumeration<String> httpParamsEnum = Collections.enumeration(Arrays.asList(httpParamNames));
         when(mockRequest.getParameterNames()).thenReturn(httpParamsEnum);
-        when(mockRequest.getParameterValues(HTTP_PARAM1)).thenReturn(new String[] {HTTP_PARAM_VALUE1});
-        when(mockRequest.getParameterValues(HTTP_PARAM2)).thenReturn(new String[] {HTTP_PARAM_VALUE2});
+        for (int i = 1; i <= httpParamNames.length; ++i) {
+            when(mockRequest.getParameterValues(HTTP_PARAM + i)).thenReturn(new String[] {HTTP_PARAM_VALUE + i});
+        }
     }
 }
\ No newline at end of file


(cxf) 02/05: Bump org.webjars:swagger-ui from 5.32.5 to 5.32.6 (#3179)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit d3e0353f6e99991f9ba46bec48d1a6c370ef86e9
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Thu Jun 4 07:23:45 2026 -0400

    Bump org.webjars:swagger-ui from 5.32.5 to 5.32.6 (#3179)
    
    Bumps [org.webjars:swagger-ui](https://github.com/swagger-api/swagger-ui) from 5.32.5 to 5.32.6.
    - [Release notes](https://github.com/swagger-api/swagger-ui/releases)
    - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.32.5...v5.32.6)
    
    ---
    updated-dependencies:
    - dependency-name: org.webjars:swagger-ui
      dependency-version: 5.32.6
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...
    
    Signed-off-by: dependabot[bot] <su...@github.com>
    Co-authored-by: dependabot[bot] <49...@users.noreply.github.com>
    (cherry picked from commit 5de63cbc5228a3b5809e26e1dbaf77eb2740ce93)
---
 distribution/src/main/release/samples/pom.xml | 2 +-
 parent/pom.xml                                | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/distribution/src/main/release/samples/pom.xml b/distribution/src/main/release/samples/pom.xml
index 3879382bdc2..d2c71eb0eb0 100644
--- a/distribution/src/main/release/samples/pom.xml
+++ b/distribution/src/main/release/samples/pom.xml
@@ -36,7 +36,7 @@
         <cxf.jetty12.version>12.0.36</cxf.jetty12.version>
         <cxf.netty.version>4.1.135.Final</cxf.netty.version>
         <cxf.httpcomponents.client.version>4.5.14</cxf.httpcomponents.client.version>
-        <cxf.swagger.ui.version>5.32.5</cxf.swagger.ui.version>
+        <cxf.swagger.ui.version>5.32.6</cxf.swagger.ui.version>
         <cxf.tika.version>3.3.1</cxf.tika.version>
         <cxf.tomcat.version>10.1.55</cxf.tomcat.version>
         <cxf.jboss.weld.version>5.1.2.Final</cxf.jboss.weld.version>
diff --git a/parent/pom.xml b/parent/pom.xml
index 4d8f849254e..7a3f205ff17 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -218,7 +218,7 @@
         <cxf.spring.security.version>6.4.13</cxf.spring.security.version>
         <cxf.spring.version>6.2.18</cxf.spring.version>
         <cxf.stax-ex.version>1.8.3</cxf.stax-ex.version>
-        <cxf.swagger.ui.version>5.32.5</cxf.swagger.ui.version>
+        <cxf.swagger.ui.version>5.32.6</cxf.swagger.ui.version>
         <cxf.swagger.v3.version>2.2.50</cxf.swagger.v3.version>
         <cxf.tika.version>3.3.1</cxf.tika.version>
         <cxf.tomcat.version>10.1.55</cxf.tomcat.version>


(cxf) 01/05: Bump org.sonarsource.scanner.maven:sonar-maven-plugin (#3178)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 77da0824ccf9d859e92dd5a63998be5afd9bbd6c
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Thu Jun 4 07:22:50 2026 -0400

    Bump org.sonarsource.scanner.maven:sonar-maven-plugin (#3178)
    
    Bumps [org.sonarsource.scanner.maven:sonar-maven-plugin](https://github.com/SonarSource/sonar-scanner-maven) from 5.6.0.6792 to 5.7.0.6970.
    - [Release notes](https://github.com/SonarSource/sonar-scanner-maven/releases)
    - [Commits](https://github.com/SonarSource/sonar-scanner-maven/compare/5.6.0.6792...5.7.0.6970)
    
    ---
    updated-dependencies:
    - dependency-name: org.sonarsource.scanner.maven:sonar-maven-plugin
      dependency-version: 5.7.0.6970
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <su...@github.com>
    Co-authored-by: dependabot[bot] <49...@users.noreply.github.com>
    (cherry picked from commit 747e09fee4591f1af90f1c58d9b0b4d9e534f2c8)
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index fa2bfb9b8ba..c2924716457 100644
--- a/pom.xml
+++ b/pom.xml
@@ -439,7 +439,7 @@
                     <plugin>
                         <groupId>org.sonarsource.scanner.maven</groupId>
                         <artifactId>sonar-maven-plugin</artifactId>
-                        <version>5.6.0.6792</version>
+                        <version>5.7.0.6970</version>
                     </plugin>
                 </plugins>
             </build>


(cxf) 03/05: Bump org.glassfish.jaxb:jaxb-xjc from 4.0.8 to 4.0.9 (#3181)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 834f1ac94afee9953db76368e5b030a6c98b8b34
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Thu Jun 4 11:40:23 2026 -0400

    Bump org.glassfish.jaxb:jaxb-xjc from 4.0.8 to 4.0.9 (#3181)
    
    * Bump org.glassfish.jaxb:jaxb-xjc from 4.0.8 to 4.0.9
    
    Bumps org.glassfish.jaxb:jaxb-xjc from 4.0.8 to 4.0.9.
    
    ---
    updated-dependencies:
    - dependency-name: org.glassfish.jaxb:jaxb-xjc
      dependency-version: 4.0.9
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...
    
    Signed-off-by: dependabot[bot] <su...@github.com>
    
    * Apply suggestions from code review
    
    Co-authored-by: Andriy Redko <dr...@gmail.com>
    
    ---------
    
    Signed-off-by: dependabot[bot] <su...@github.com>
    Co-authored-by: dependabot[bot] <49...@users.noreply.github.com>
    Co-authored-by: Andriy Redko <dr...@gmail.com>
    (cherry picked from commit d43cd04b39e7581b2821b119bae88dbd7aa2ebeb)
    
    # Conflicts:
    #       parent/pom.xml
---
 parent/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parent/pom.xml b/parent/pom.xml
index 7a3f205ff17..3ed0459f7ef 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -151,7 +151,7 @@
         <cxf.javassist.version>3.29.2-GA</cxf.javassist.version>
         <cxf.jakarta.annotation-api.version>2.1.1</cxf.jakarta.annotation-api.version>
         <cxf.jakarta.ws.rs.version>3.1.0</cxf.jakarta.ws.rs.version>
-        <cxf.jaxb.runtime.version>4.0.8</cxf.jaxb.runtime.version>
+        <cxf.jaxb.runtime.version>4.0.9</cxf.jaxb.runtime.version>
         <cxf.jaxb.version>4.0.5</cxf.jaxb.version>
         <cxf.jaxb.xjc.version>${cxf.jaxb.runtime.version}</cxf.jaxb.xjc.version>
         <cxf.jaxen.version>2.0.4</cxf.jaxen.version>


(cxf) 05/05: Recording .gitmergeinfo Changes

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit eb6aa44620385c15f649e35e0d82b739bdfa6ad1
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Thu Jun 4 12:02:09 2026 -0400

    Recording .gitmergeinfo Changes
---
 .gitmergeinfo | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitmergeinfo b/.gitmergeinfo
index 9df3130d610..3e533541c12 100644
--- a/.gitmergeinfo
+++ b/.gitmergeinfo
@@ -134,6 +134,7 @@ M 5c1266b0e9815ec4bd02b4fb6ad4d534f01855b3
 M 5c6e17ec3b9e65bbf1722cb42d0e9ce48038abdf
 M 5ce2625d759ffd64224489448d200ae5bea7ec99
 M 5d5595d9e62a46a1579b434bdccf913c75f8b0c0
+M 5de63cbc5228a3b5809e26e1dbaf77eb2740ce93
 M 5f1b27db2666910e707cd009e11a07eba0225a49
 M 5fd39e8945167a695eb4bab260dde8709ed1dcea
 M 602954b3b8bb1a6e832edb2cfc2bde0cbebe6fd1
@@ -277,6 +278,7 @@ M ce1d1b80df3dc553d3287cf89408264f530b1a12
 M cf76b0906d4c8d310df17748c9c171633f951012
 M d0945603c2c1a14ee6801992a4d7984737ba6fd4
 M d303dac7fc573430954f2b8bdfd7cfc7f2247d0f
+M d43cd04b39e7581b2821b119bae88dbd7aa2ebeb
 M d538346d757bcfc7d9e4a3cd44e06f2060acfd59
 M d5697f5065f2cde0859276999066c5e81f39246b
 M d687e4081fa40341936615bf0daaeebf9e38f62f