aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-process
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2019-09-18 10:52:44 -0500
committersonartech <sonartech@sonarsource.com>2019-09-20 10:42:52 +0200
commit10a1454ce0e0b97f00e2792942f1e2131bc877db (patch)
treef543f2743f780320816f832de88cf36d6246edba /server/sonar-process
parent0443794329122f49b910eb58cb505349ab116414 (diff)
downloadsonarqube-10a1454ce0e0b97f00e2792942f1e2131bc877db.tar.gz
sonarqube-10a1454ce0e0b97f00e2792942f1e2131bc877db.zip
SSF-92 Code injection from SonarQube plugins
Diffstat (limited to 'server/sonar-process')
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/SecurityManagement.java65
-rw-r--r--server/sonar-process/src/test/java/org/sonar/process/SecurityManagementTest.java70
2 files changed, 135 insertions, 0 deletions
diff --git a/server/sonar-process/src/main/java/org/sonar/process/SecurityManagement.java b/server/sonar-process/src/main/java/org/sonar/process/SecurityManagement.java
new file mode 100644
index 00000000000..e8f49e0d6d8
--- /dev/null
+++ b/server/sonar-process/src/main/java/org/sonar/process/SecurityManagement.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import java.lang.reflect.ReflectPermission;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SecurityManagement {
+ private SecurityManagement() {
+ // static only
+ }
+
+ public static void restrictPlugins() {
+ SecurityManager sm = new SecurityManager();
+ Policy.setPolicy(new CustomPolicy());
+ System.setSecurityManager(sm);
+ }
+
+ static class CustomPolicy extends Policy {
+ private static final Set<String> ALLOWED_RUNTIME_PERMISSIONS = new HashSet<>(Arrays.asList("getFileSystemAttributes", "readFileDescriptor", "writeFileDescriptor",
+ "getStackTrace", "setDefaultUncaughtExceptionHandler", "manageProcess", "localeServiceProvider", "LoggerFinder"));
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ // classloader used to load plugins
+ String clName = getDomainClassLoaderName(domain);
+ if ("org.sonar.classloader.ClassRealm".equals(clName)) {
+ if (permission instanceof RuntimePermission && !ALLOWED_RUNTIME_PERMISSIONS.contains(permission.getName())) {
+ return false;
+ }
+ if (permission instanceof ReflectPermission || permission instanceof SecurityPermission) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String getDomainClassLoaderName(ProtectionDomain domain) {
+ return domain.getClassLoader().getClass().getName();
+ }
+ }
+}
diff --git a/server/sonar-process/src/test/java/org/sonar/process/SecurityManagementTest.java b/server/sonar-process/src/test/java/org/sonar/process/SecurityManagementTest.java
new file mode 100644
index 00000000000..afe142b4ea6
--- /dev/null
+++ b/server/sonar-process/src/test/java/org/sonar/process/SecurityManagementTest.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.process;
+
+import java.lang.reflect.ReflectPermission;
+import java.security.Permission;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+
+public class SecurityManagementTest {
+ private ClassLoader classRealm = mock(ClassLoader.class, RETURNS_DEEP_STUBS);
+ private ProtectionDomain pd = new ProtectionDomain(null, null, classRealm, null);
+
+ private Permission allowedRuntime = new RuntimePermission("getFileSystemAttributes");
+ private Permission deniedRuntime = new RuntimePermission("getClassLoader");
+ private Permission reflect = new ReflectPermission("suppressAccessChecks");
+ private Permission security = new SecurityPermission("setPolicy");
+
+ @Test
+ public void policy_restricts_class_realm() {
+ SecurityManagement.CustomPolicy policy = new SecurityManagement.CustomPolicy() {
+ @Override
+ String getDomainClassLoaderName(ProtectionDomain domain) {
+ return "org.sonar.classloader.ClassRealm";
+ }
+ };
+
+ assertThat(policy.implies(pd, allowedRuntime)).isTrue();
+ assertThat(policy.implies(pd, deniedRuntime)).isFalse();
+ assertThat(policy.implies(pd, reflect)).isFalse();
+ assertThat(policy.implies(pd, security)).isFalse();
+ }
+
+ @Test
+ public void policy_does_not_restrict_other_classloaders() {
+ SecurityManagement.CustomPolicy policy = new SecurityManagement.CustomPolicy() {
+ @Override
+ String getDomainClassLoaderName(ProtectionDomain domain) {
+ return "classloader";
+ }
+ };
+
+ assertThat(policy.implies(pd, allowedRuntime)).isTrue();
+ assertThat(policy.implies(pd, deniedRuntime)).isTrue();
+ assertThat(policy.implies(pd, reflect)).isTrue();
+ assertThat(policy.implies(pd, security)).isTrue();
+ }
+}