Question

I'm working on a project with heavy security constraints. A requirement is to seal our jars.

Since we sealed jars, a lot of our junit-tests failed with the following error :

java.lang.SecurityException: sealing violation: package [a.dependency.package] is sealed
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:234)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.mockito.cglib.core.ReflectUtils.addAllMethods(ReflectUtils.java:349)
    at org.mockito.cglib.proxy.Enhancer.getMethods(Enhancer.java:422)
    at org.mockito.cglib.proxy.Enhancer.generateClass(Enhancer.java:457)
    at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
    at org.mockito.cglib.proxy.Enhancer.createHelper(Enhancer.java:378)
    at org.mockito.cglib.proxy.Enhancer.createClass(Enhancer.java:318)
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxyClass(ClassImposterizer.java:93)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:50)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:54)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:45)
    at org.mockito.Mockito.spy(Mockito.java:991)
    at [...]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

It looks like the problem is caused by Mockito :

We "mock" and "spy" classes coming from some external sealed jars, and the "mock classes" generated by Mockito have the same package than the "mocked classes".

Because the package from the dependency jar is sealed, the tested jar cannot create a class in the same package (the URLClassLoader check that the same package is not used from different sealed jars).

I tried to add a specific SecurityManager .policy file for junit testing, but I didn't found a property allowing to have classes inside a package already sealed by a dependency.

Moreover, it seems that the URLClassLoader has no option to remove sealing violation check.

The version of Mockito we use is 1.8.5. I tried to use the latest version (1.9.5) but it didn't fix the error.

If someone has an idea...

Was it helpful?

Solution

You can work around this by putting traits in a different package and when testing mock that trait instead of the classes in your sealed jar. That could be construed as a security concern but you can argue against that quite easily.

You could seal a jar that imports all your unsealed code and not add any behavior there. That would give your customer the security he needs, but avoid you having to deal with the restrictions in tests or other implementations where you don't have this requirement.

I'm sure you're using dependencies that are unsealed, so you just make your own testable code a dependency of the sealed jar too.

OTHER TIPS

You could consider ways to run these tests against code that's not in sealed jars. Either build unsealed jars as well, and use them to test, or unpack the jars before testing.

I had to deal with Mockito.spy and signed jars recently.

Actually one solution would be to force the test task to unsign all the problematic jars (i.e. those signed).


With Apache Ant you can define a macro

<macrodef name="unsignjar">
    <attribute name="jarFile" />
    <sequential>
        <jar update="yes"
             jarfile="@{jarFile}.tmp">
            <zipfileset src="@{jarFile}">
                <include name="**"/>
                <exclude name="META-INF/*.SF"/>
                <exclude name="META-INF/*.DSA"/>
                <exclude name="META-INF/*.RSA"/>
            </zipfileset>
        </jar>
        <move file="@{jarFile}.tmp"
              tofile="@{jarFile}"
              overwrite="true" />
    </sequential>
</macrodef>

and then call it like in the following:

<target name="unsign-problematic-jars">
    <unsignjar jarFile="my-signed.jar" />
</target>

With Apache Maven, you can do as suggested in this answer, so you resort to the maven-jarsigner-plugin plugin.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top