Mention explicitly that general regex aren't supported

The general regex were neither explicitly allowed nor forbidden;
add a specific note in the documentation and checks in the code
and tests for making sure that the only general regex allowed
is ^.*

Change-Id: I61d51430e5586f805a4f7691ac6c865e0afc78b8
diff --git a/README.md b/README.md
index e94a03e..a51efd3 100644
--- a/README.md
+++ b/README.md
@@ -63,3 +63,9 @@
 [default]
   projects = ^.*
 ```
+
+> **NOTE**: The `^.*` is the only regular expression supported by the virtualhost
+> module because of the potential performance implication of a generic regular expression
+> evaluation during the ACLs. Bear in mind that any possible action perform in Gerrit will
+> go through the virtualhost module filtering and therefore it is paramount to minimize the
+> potential performance impact.
diff --git a/src/main/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfig.java b/src/main/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfig.java
index d2d1cc9..3b80f7c 100644
--- a/src/main/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfig.java
+++ b/src/main/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfig.java
@@ -15,6 +15,7 @@
 package com.gerritforge.gerrit.modules.virtualhost;
 
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.project.RefPattern;
 import com.google.inject.Inject;
 import java.io.File;
 import java.io.IOException;
@@ -31,6 +32,7 @@
   private final Config config;
   private final boolean enabled;
 
+  public static final String ALL_PROJECTS_REGEX = "^.*";
   public final String[] defaultProjects;
 
   @Inject
@@ -46,7 +48,7 @@
       defaultProjects = new String[0];
       return;
     }
-    defaultProjects = config.getStringList("default", null, "projects");
+    defaultProjects = validateProjects(config.getStringList("default", null, "projects"));
     enabled = !config.getSubsections("server").isEmpty() || defaultProjects.length > 0;
   }
 
@@ -55,7 +57,7 @@
       return EMPTY_PROJECTS_ARRAY;
     }
 
-    String[] projects = config.getStringList("server", hostname, "projects");
+    String[] projects = validateProjects(config.getStringList("server", hostname, "projects"));
     if (projects.length > 0) {
       return projects;
     }
@@ -63,6 +65,16 @@
     return defaultProjects;
   }
 
+  private String[] validateProjects(String[] projects) {
+    for (String project : projects) {
+      if (RefPattern.isRE(project) && !project.equals(ALL_PROJECTS_REGEX)) {
+        throw new IllegalStateException(
+            "Project regex '" + project + "' is not allowed in virtualhost.config");
+      }
+    }
+    return projects;
+  }
+
   public boolean isEnabled() {
     return enabled;
   }
diff --git a/src/test/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfigTest.java b/src/test/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfigTest.java
new file mode 100644
index 0000000..d9ecbf2
--- /dev/null
+++ b/src/test/java/com/gerritforge/gerrit/modules/virtualhost/VirtualHostConfigTest.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2024 GerritForge Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.gerritforge.gerrit.modules.virtualhost;
+
+import static com.gerritforge.gerrit.modules.virtualhost.VirtualHostConfig.ALL_PROJECTS_REGEX;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.server.config.SitePaths;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VirtualHostConfigTest {
+
+  public static final String TEST_VIRTUAL_HOST = "testhost";
+  private SitePaths testSitePaths;
+  private FileBasedConfig virtualHostFileConfig;
+
+  @Before
+  public void setup() throws IOException {
+    Path tempPath = Files.createTempDirectory("virtualhost-test-" + System.nanoTime());
+    testSitePaths = new SitePaths(tempPath);
+    virtualHostFileConfig =
+        new FileBasedConfig(
+            testSitePaths.etc_dir.resolve("virtualhost.config").toFile(), FS.DETECTED);
+  }
+
+  @Test
+  public void projectsAllowsCatchAllRegExOnDefaults() throws IOException {
+    setVirtualHostConfig("default", null, ALL_PROJECTS_REGEX);
+    assertThat(new VirtualHostConfig(testSitePaths).getProjects(TEST_VIRTUAL_HOST))
+        .isEqualTo(new String[] {ALL_PROJECTS_REGEX});
+  }
+
+  @Test
+  public void projectsAllowsCatchAllRegExOnSpecificHost() throws IOException {
+    setVirtualHostConfig("server", TEST_VIRTUAL_HOST, ALL_PROJECTS_REGEX);
+    assertThat(new VirtualHostConfig(testSitePaths).getProjects(TEST_VIRTUAL_HOST))
+        .isEqualTo(new String[] {ALL_PROJECTS_REGEX});
+  }
+
+  @Test
+  public void projectsGeneralRegExOnDefaultsShouldBeForbidden() throws IOException {
+    setVirtualHostConfig("default", null, "^ageneralregex.*$");
+    assertThrows(
+        IllegalStateException.class,
+        () -> new VirtualHostConfig(testSitePaths).getProjects(TEST_VIRTUAL_HOST));
+  }
+
+  @Test
+  public void projectsGeneralRegExOnSpecificHostShouldBeForbidden() throws IOException {
+    setVirtualHostConfig("server", TEST_VIRTUAL_HOST, "^ageneralregex.*$");
+    assertThrows(
+        IllegalStateException.class,
+        () -> new VirtualHostConfig(testSitePaths).getProjects(TEST_VIRTUAL_HOST));
+  }
+
+  private void setVirtualHostConfig(String section, String subsection, String projectsValue)
+      throws IOException {
+    virtualHostFileConfig.setString(section, subsection, "projects", projectsValue);
+    virtualHostFileConfig.save();
+  }
+}
OSZAR »