Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed security manager (deprecated) #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 3 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ void yourtest() {

Features:
- CPU timeouts on the code
- Jails student code
- No I/O, including stdout/err
- No thread creating by the student, ...
- Most things involving syscalls are forbidden
- specific permissions can be added on specifics tests if needed
- Text/RST reporting
- Custom feedback, both from outside the test (onFail, onTimeout, ...) but also from inside (see below).

Expand Down Expand Up @@ -76,6 +71,9 @@ TOTAL WITHOUT IGNORED 5/8
--- END GRADE ---
```

Beware that students can still print a fake output such as the one above.
**Please parse the last lines from the standard output to retrieve the correct grade details**.

## Documentation & installation

Everything needed is located inside the files:
Expand Down Expand Up @@ -271,60 +269,3 @@ public class RunTests {
}
}
```

### Custom permissions

JavaGrading installs a custom [SecurityManager](https://docs.oracle.com/javase/8/docs/api/java/lang/SecurityManager.html)
that forbids the tested code to do anything that it should not do.

It effectively forbids [a lot of things](https://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html).
JavaGrading adds an additionnal permission to this list, namely `PrintPermission`, that allows the test code to
print things on stdout/stderr.

You can re-enable some permissions for a specific test if needed, but it does requires some boilerplate:

```java
@RunWith(GradingRunner.class)
public class PermissionTest {
@Test
@Grade(value = 5.0, customPermissions = MyPerms1.class)
public void allowPrint() {
System.out.println("I was allowed to print!");
}

@Test
@Grade(value = 5.0, customPermissions = MyPerms2.class)
public void allowThread() {
Thread t = new Thread() {
@Override
public void run() {
// nothing
}
};
t.start();
}

/*
NOTE: the class MUST be public AND static (if it is an inner class) for this to work.
=> it must have an accessible constructor without args.
*/
public static class MyPerms1 implements Grade.PermissionCollectionFactory {
@Override
public PermissionCollection get() {
Permissions perms = new Permissions();
perms.add(PrintPermission.instance);
return perms;
}
}

public static class MyPerms2 implements Grade.PermissionCollectionFactory {
@Override
public PermissionCollection get() {
Permissions perms = new Permissions();
perms.add(new RuntimePermission("modifyThreadGroup"));
perms.add(new RuntimePermission(("modifyThread")));
return perms;
}
}
}
```
24 changes: 0 additions & 24 deletions src/main/java/com/github/guillaumederval/javagrading/Grade.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.security.PermissionCollection;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
Expand All @@ -31,27 +30,4 @@
*/
boolean custom() default false;

/**
* Overrides permissions. Not taken into account if PermissionCollectionFactory.get() returns null.
*
* The class should be instantiable without args.
*
* By default, tests have no particular permissions, i.e. they can't do anything fancy with the JVM.
*
* Note: if you allow modifyThreadGroup/modifyThread and setIO, you may break some components of JavaGrading,
* namely the protection against stdout/stderr usage and the cpu timeout management. Reflection is also a problem,
* and other permissions may allow tests to jailbreak. Use with caution.
*/
Class<? extends PermissionCollectionFactory> customPermissions() default NullPermissionCollectionFactory.class;

interface PermissionCollectionFactory {
PermissionCollection get();
}

class NullPermissionCollectionFactory implements PermissionCollectionFactory {
@Override
public PermissionCollection get() {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.github.guillaumederval.javagrading;

import com.github.guillaumederval.javagrading.utils.PrintPermission;
import org.junit.Test;
import org.junit.internal.runners.statements.FailOnTimeout;
import org.junit.runners.model.FrameworkMethod;
Expand All @@ -9,9 +8,6 @@

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.InvocationTargetException;
import java.security.*;
import java.security.cert.Certificate;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
Expand All @@ -23,7 +19,7 @@
*/
class GradingRunnerUtils {
static Statement methodInvoker(FrameworkMethod method, Statement base) {
return cpu(method, jail(method, base));
return cpu(method, base);
}

static Statement methodBlock(FrameworkMethod method, Statement base) {
Expand Down Expand Up @@ -89,59 +85,4 @@ public void evaluate() throws Throwable {
return base;
}

private static Statement jail(FrameworkMethod method, final Statement base) {
checkSecurity();

final Grade g = method.getAnnotation(Grade.class);

PermissionCollection coll = null;
if(g != null) {
try {
coll = g.customPermissions().getConstructor().newInstance().get();
}
catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ignored) {
//ignored
}
}

if(coll == null)
coll = new Permissions();
if(g != null && g.debug())
coll.add(PrintPermission.instance);

ProtectionDomain pd = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), coll);

return new Statement() {
@Override
public void evaluate() throws Throwable {

Throwable ex = AccessController.doPrivileged(new PrivilegedExceptionAction<Throwable>() {
@Override
public Throwable run() throws Exception {
Throwable ex = null;
try {
base.evaluate();
} catch (Throwable throwable) {
ex = throwable;
}
return ex;
}
}, new AccessControlContext(new ProtectionDomain[]{pd}));

if(ex != null)
throw ex;
}
};
}

private static void checkSecurity() {
if(!(System.getSecurityManager() instanceof TestSecurityManager)) {
try {
System.setSecurityManager(new TestSecurityManager());
}
catch (SecurityException e) {
System.out.println("/!\\ WARNING: Cannot set a TestSecurityManager as the security manager. Tests may not be jailed properly.");
}
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading