mockito vs sealed packages
Asked Answered
A

3

12

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...

Adamok answered 4/3, 2013 at 16:8 Comment(6)
What version of Mockito are you running?Martinamartindale
On a somewhat related note, why did you choose to seal your jars? On an unrelated note... Welcome to stackoverflow, have some rep!Boorman
For jar sealing, it is a requirement from the customer. He invoked security matters. By example : injecting jars to the classpath could cause some external code accessing package private code. The customer choice is not really clear for me, so my answer can be confuse.Adamok
For the mockito version, we use the 1.8.5. We may be able to upgrade the version if needed. (sorry for the triple post, I am not fluent in english, and I couldn't edit it before the 5 minutes delay)Adamok
I've tested a simple mock of an interface from a sealed package, and a mock and a spy of a class from a sealed package, on both 1.8.5 and 1.9.5 of Mockito, and was able to do so without problems. Perhaps you can add some code of your test?Martinamartindale
Thank you for your help, I did the same, and it didn't throwed an exception... I will investigate deeply on this error, and post new elements if I find what is wrong. I cannot post any code from the project as the sources are confidential.Adamok
W
3

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.

Weingartner answered 5/3, 2013 at 20:46 Comment(1)
Finally, we decided to have an unsealed copy of our jars for test purposes. The sealed jars will be included in the "production distribution". Thank you for your help !Adamok
P
1

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.

Po answered 9/3, 2013 at 11:16 Comment(0)
B
0

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.

Bricole answered 20/12, 2016 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.