Limit size of "message" field in PostCheck to a default of 10k

The limit can be set in gerrit.config as plugin.checks.messageSizeLimit.

Change-Id: I71d707c34049a81cfe62d50a2b53769c79aeae1d
diff --git a/java/com/google/gerrit/plugins/checks/api/PostCheck.java b/java/com/google/gerrit/plugins/checks/api/PostCheck.java
index aaad490..cf7cbd3 100644
--- a/java/com/google/gerrit/plugins/checks/api/PostCheck.java
+++ b/java/com/google/gerrit/plugins/checks/api/PostCheck.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.UserInitiated;
 import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import java.io.IOException;
@@ -55,6 +56,7 @@
   private final Checks checks;
   private final Provider<ChecksUpdate> checksUpdate;
   private final CheckJson.Factory checkJsonFactory;
+  private final PluginConfigFactory pluginConfigFactory;
 
   @Inject
   PostCheck(
@@ -64,7 +66,8 @@
       Checkers checkers,
       Checks checks,
       @UserInitiated Provider<ChecksUpdate> checksUpdate,
-      CheckJson.Factory checkJsonFactory) {
+      CheckJson.Factory checkJsonFactory,
+      PluginConfigFactory pluginConfigFactory) {
     this.self = self;
     this.permissionBackend = permissionBackend;
     this.permission = permission;
@@ -72,6 +75,7 @@
     this.checks = checks;
     this.checksUpdate = checksUpdate;
     this.checkJsonFactory = checkJsonFactory;
+    this.pluginConfigFactory = pluginConfigFactory;
   }
 
   @Override
@@ -102,6 +106,7 @@
     CheckKey key = CheckKey.create(rsrc.getProject(), rsrc.getPatchSet().id(), checkerUuid);
     Optional<Check> check = checks.getCheck(key, GetCheckOptions.defaults());
     Check updatedCheck;
+    CheckUpdate checkUpdate = toCheckUpdate(input);
     if (!check.isPresent()) {
       checkers
           .getChecker(checkerUuid)
@@ -110,19 +115,15 @@
                   new UnprocessableEntityException(
                       String.format("checker %s not found", checkerUuid)));
       updatedCheck =
-          checksUpdate
-              .get()
-              .createCheck(key, toCheckUpdate(input), input.notify, input.notifyDetails);
+          checksUpdate.get().createCheck(key, checkUpdate, input.notify, input.notifyDetails);
     } else {
       updatedCheck =
-          checksUpdate
-              .get()
-              .updateCheck(key, toCheckUpdate(input), input.notify, input.notifyDetails);
+          checksUpdate.get().updateCheck(key, checkUpdate, input.notify, input.notifyDetails);
     }
     return Response.ok(checkJsonFactory.noOptions().format(updatedCheck));
   }
 
-  private static CheckUpdate toCheckUpdate(CheckInput input) throws BadRequestException {
+  private CheckUpdate toCheckUpdate(CheckInput input) throws BadRequestException {
     CheckUpdate.Builder checkUpdateBuilder = CheckUpdate.builder();
 
     if (input.state != null) {
@@ -130,7 +131,9 @@
     }
 
     if (input.message != null) {
-      checkUpdateBuilder.setMessage(input.message.trim());
+      String message = input.message.trim();
+      checkMessageSizeLimit(message.length());
+      checkUpdateBuilder.setMessage(message);
     }
 
     if (input.url != null) {
@@ -147,4 +150,14 @@
 
     return checkUpdateBuilder.build();
   }
+
+  private void checkMessageSizeLimit(int messageSize) throws BadRequestException {
+    int messageSizeLimit =
+        pluginConfigFactory.getFromGerritConfig("checks").getInt("messageSizeLimit", 10_000);
+    if (messageSize > messageSizeLimit) {
+      throw new BadRequestException(
+          String.format(
+              "Field \"message\" exceeds size limit (%d > %d)", messageSize, messageSizeLimit));
+    }
+  }
 }
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
index ba01396..c763abc 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
@@ -18,6 +18,7 @@
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.gerrit.acceptance.UseClockStep;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.entities.PatchSet;
@@ -98,6 +99,31 @@
   }
 
   @Test
+  public void updateMessage_rejected_tooLong() {
+    CheckInput input = new CheckInput();
+    input.message = new String(new char[10_001]).replace('\0', 'x');
+
+    BadRequestException thrown =
+        assertThrows(
+            BadRequestException.class,
+            () -> checksApiFactory.revision(patchSetId).id(checkKey.checkerUuid()).update(input));
+    assertThat(thrown).hasMessageThat().contains("exceeds size limit (10001 > 10000)");
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.checks.messageSizeLimit", value = "5")
+  public void updateMessage_rejected_configureLimit() {
+    CheckInput input = new CheckInput();
+    input.message = "foobar";
+
+    BadRequestException thrown =
+        assertThrows(
+            BadRequestException.class,
+            () -> checksApiFactory.revision(patchSetId).id(checkKey.checkerUuid()).update(input));
+    assertThat(thrown).hasMessageThat().contains("exceeds size limit (6 > 5)");
+  }
+
+  @Test
   public void unsetMessage() throws Exception {
     checkOperations.check(checkKey).forUpdate().message("some message").upsert();
 
diff --git a/resources/Documentation/rest-api-checks.md b/resources/Documentation/rest-api-checks.md
index 0d35ac8..c400b34 100644
--- a/resources/Documentation/rest-api-checks.md
+++ b/resources/Documentation/rest-api-checks.md
@@ -182,7 +182,7 @@
 | `patch_set_id`        |          | The patch set that this check applies to.
 | `checker_uuid`        |          | The [UUID](./rest-api-checkers.md#checker-id) of the checker that reported this check.
 | `state`               |          | The state as string-serialized form of [CheckState](#check-state)
-| `message`             | optional | Short message explaining the check state.
+| `message`             | optional | Short message explaining the check state. Size limit is 10k by default, configured via plugin.checks.messageSizeLimit.
 | `url`                 | optional | A fully-qualified URL pointing to the result of the check on the checker's infrastructure.
 | `started`             | optional | The [timestamp](../../../Documentation/rest-api.html#timestamp) of when the check started processing.
 | `finished`            | optional | The [timestamp](../../../Documentation/rest-api.html#timestamp) of when the check finished processing.