Add SecureSM support for newer IDEA versions (#49747)
IntelliJ IDEA moved their JUnit runner to a different package. While this does not break running tests in IDEA, it leads to an ugly exception being thrown at the end of the tests: Exception in thread "main" java.lang.SecurityException: java.lang.System#exit(0) calls are not allowed at org.elasticsearch.secure_sm.SecureSM$2.run(SecureSM.java:248) at org.elasticsearch.secure_sm.SecureSM$2.run(SecureSM.java:215) at java.base/java.security.AccessController.doPrivileged(AccessController.java:310) at org.elasticsearch.secure_sm.SecureSM.innerCheckExit(SecureSM.java:215) at org.elasticsearch.secure_sm.SecureSM.checkExit(SecureSM.java:206) at java.base/java.lang.Runtime.exit(Runtime.java:111) at java.base/java.lang.System.exit(System.java:1781) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:59) This commit adds support for newer IDEA versions in SecureSM.
This commit is contained in:
parent
aa443c6362
commit
6dcb7fa50e
|
@ -46,16 +46,16 @@ import java.util.Objects;
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code modifyThread} and {@code modifyThreadGroup} are required for any thread access
|
* <li>{@code modifyThread} and {@code modifyThreadGroup} are required for any thread access
|
||||||
* checks: with these permissions, access is granted as long as the thread group is
|
* checks: with these permissions, access is granted as long as the thread group is
|
||||||
* the same or an ancestor ({@code sourceGroup.parentOf(targetGroup) == true}).
|
* the same or an ancestor ({@code sourceGroup.parentOf(targetGroup) == true}).
|
||||||
* <li>code without these permissions can do very little, except to interrupt itself. It may
|
* <li>code without these permissions can do very little, except to interrupt itself. It may
|
||||||
* not even create new threads.
|
* not even create new threads.
|
||||||
* <li>very special cases (like test runners) that have {@link ThreadPermission} can violate
|
* <li>very special cases (like test runners) that have {@link ThreadPermission} can violate
|
||||||
* threadgroup security rules.
|
* threadgroup security rules.
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager
|
* If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager
|
||||||
* is installed, it will emit additional debugging information when threadgroup access checks fail.
|
* is installed, it will emit additional debugging information when threadgroup access checks fail.
|
||||||
*
|
*
|
||||||
* @see SecurityManager#checkAccess(Thread)
|
* @see SecurityManager#checkAccess(Thread)
|
||||||
* @see SecurityManager#checkAccess(ThreadGroup)
|
* @see SecurityManager#checkAccess(ThreadGroup)
|
||||||
* @see <a href="http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html">
|
* @see <a href="http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html">
|
||||||
|
@ -105,8 +105,10 @@ public class SecureSM extends SecurityManager {
|
||||||
"com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
|
"com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
|
||||||
// eclipse test runner
|
// eclipse test runner
|
||||||
"org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
|
"org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
|
||||||
// intellij test runner
|
// intellij test runner (before IDEA version 2019.3)
|
||||||
"com\\.intellij\\.rt\\.execution\\.junit\\..*"
|
"com\\.intellij\\.rt\\.execution\\.junit\\..*",
|
||||||
|
// intellij test runner (since IDEA version 2019.3)
|
||||||
|
"com\\.intellij\\.rt\\.junit\\..*"
|
||||||
};
|
};
|
||||||
|
|
||||||
// java.security.debug support
|
// java.security.debug support
|
||||||
|
@ -122,7 +124,7 @@ public class SecureSM extends SecurityManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
||||||
public void checkAccess(Thread t) {
|
public void checkAccess(Thread t) {
|
||||||
|
@ -137,7 +139,7 @@ public class SecureSM extends SecurityManager {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
||||||
public void checkAccess(ThreadGroup g) {
|
public void checkAccess(ThreadGroup g) {
|
||||||
|
@ -157,7 +159,7 @@ public class SecureSM extends SecurityManager {
|
||||||
System.err.println("access: caller group=" + caller);
|
System.err.println("access: caller group=" + caller);
|
||||||
System.err.println("access: target group=" + target);
|
System.err.println("access: target group=" + target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// thread permission logic
|
// thread permission logic
|
||||||
|
|
||||||
private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
|
private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
|
||||||
|
@ -168,31 +170,31 @@ public class SecureSM extends SecurityManager {
|
||||||
|
|
||||||
// first, check if we can modify threads at all.
|
// first, check if we can modify threads at all.
|
||||||
checkPermission(MODIFY_THREAD_PERMISSION);
|
checkPermission(MODIFY_THREAD_PERMISSION);
|
||||||
|
|
||||||
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
||||||
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
||||||
final ThreadGroup target = t.getThreadGroup();
|
final ThreadGroup target = t.getThreadGroup();
|
||||||
|
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
return; // its a dead thread, do nothing.
|
return; // its a dead thread, do nothing.
|
||||||
} else if (source.parentOf(target) == false) {
|
} else if (source.parentOf(target) == false) {
|
||||||
checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION);
|
checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup");
|
private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup");
|
||||||
private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup");
|
private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup");
|
||||||
|
|
||||||
protected void checkThreadGroupAccess(ThreadGroup g) {
|
protected void checkThreadGroupAccess(ThreadGroup g) {
|
||||||
Objects.requireNonNull(g);
|
Objects.requireNonNull(g);
|
||||||
|
|
||||||
// first, check if we can modify thread groups at all.
|
// first, check if we can modify thread groups at all.
|
||||||
checkPermission(MODIFY_THREADGROUP_PERMISSION);
|
checkPermission(MODIFY_THREADGROUP_PERMISSION);
|
||||||
|
|
||||||
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
||||||
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
||||||
final ThreadGroup target = g;
|
final ThreadGroup target = g;
|
||||||
|
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return; // we are a dead thread, do nothing
|
return; // we are a dead thread, do nothing
|
||||||
} else if (source.parentOf(target) == false) {
|
} else if (source.parentOf(target) == false) {
|
||||||
|
@ -205,7 +207,7 @@ public class SecureSM extends SecurityManager {
|
||||||
public void checkExit(int status) {
|
public void checkExit(int status) {
|
||||||
innerCheckExit(status);
|
innerCheckExit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "Uwe Schindler" algorithm.
|
* The "Uwe Schindler" algorithm.
|
||||||
*
|
*
|
||||||
|
@ -227,7 +229,7 @@ public class SecureSM extends SecurityManager {
|
||||||
exitMethodHit = className + '#' + methodName + '(' + status + ')';
|
exitMethodHit = className + '#' + methodName + '(' + status + ')';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitMethodHit != null) {
|
if (exitMethodHit != null) {
|
||||||
if (classesThatCanExit == null) {
|
if (classesThatCanExit == null) {
|
||||||
break;
|
break;
|
||||||
|
@ -240,7 +242,7 @@ public class SecureSM extends SecurityManager {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitMethodHit == null) {
|
if (exitMethodHit == null) {
|
||||||
// should never happen, only if JVM hides stack trace - replace by generic:
|
// should never happen, only if JVM hides stack trace - replace by generic:
|
||||||
exitMethodHit = "JVM exit method";
|
exitMethodHit = "JVM exit method";
|
||||||
|
@ -248,7 +250,7 @@ public class SecureSM extends SecurityManager {
|
||||||
throw new SecurityException(exitMethodHit + " calls are not allowed");
|
throw new SecurityException(exitMethodHit + " calls are not allowed");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// we passed the stack check, delegate to super, so default policy can still deny permission:
|
// we passed the stack check, delegate to super, so default policy can still deny permission:
|
||||||
super.checkExit(status);
|
super.checkExit(status);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue