rysang / mockito

Automatically exported from code.google.com/p/mockito
0 stars 0 forks source link

SecurityException when using spy() on class from signed JAR file #393

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Create a spy for a class from a signed jar (Table mySpy = 
Mockito.spy(Table.class))
2. Define behavior (when(mySpy.foo()).thenReturn(x))

This results in the stacktrace shown below. When Table.class comes from an 
unsigend JAR file then everything works fine.

org.mockito.cglib.core.CodeGenerationException: 
java.lang.reflect.InvocationTargetException-->null
    at myCompany.MyTableTest.init(MyTableTest.java:345)
    at myCompany.MyTableTest.twoColumnsShareSpaceEqually(MyTableTest.java:99)
    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.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:43)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    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)
Caused by: java.lang.reflect.InvocationTargetException
    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.mockito.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:385)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:220)
    at org.mockito.cglib.reflect.FastClass$Generator.create(FastClass.java:65)
    at org.mockito.cglib.proxy.MethodProxy.helper(MethodProxy.java:121)
    at org.mockito.cglib.proxy.MethodProxy.init(MethodProxy.java:75)
    at org.mockito.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:214)
    at org.mockito.internal.creation.AbstractMockitoMethodProxy.invokeSuper(AbstractMockitoMethodProxy.java:10)
    at org.mockito.internal.invocation.realmethod.CGLIBProxyRealMethod.invoke(CGLIBProxyRealMethod.java:22)
    at org.mockito.internal.invocation.realmethod.FilteredCGLIBProxyRealMethod.invoke(FilteredCGLIBProxyRealMethod.java:27)
    at org.mockito.internal.invocation.Invocation.callRealMethod(Invocation.java:213)
    at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:36)
    at org.mockito.internal.MockHandler.handle(MockHandler.java:101)
    at org.mockito.internal.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:36)
    at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:48)
    at codegen.org.eclipse.swt.widgets.Table$$EnhancerByMockitoWithCGLIB$$6801b805.getBounds(<generated>)
    ... 26 more
Caused by: java.lang.SecurityException: class 
"org.eclipse.swt.widgets.Control$$FastClassByMockitoWithCGLIB$$891f76f"'s 
signer information does not match signer information of other classes in the 
same package
    at java.lang.ClassLoader.checkCerts(ClassLoader.java:807)
    at java.lang.ClassLoader.preDefineClass(ClassLoader.java:488)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:626)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
    ... 45 more

Original issue reported on code.google.com by jebo0...@googlemail.com on 7 Nov 2012 at 12:36

GoogleCodeExporter commented 8 years ago
Hi,

Are you running this signed jar in an OSGI container ?

Original comment by brice.du...@gmail.com on 7 Nov 2012 at 1:11

GoogleCodeExporter commented 8 years ago
No - the test is executed in Eclipse but using "Run as JUnit...". So a "normal" 
JVM is launched without any OSGi class loading in place. 

Otherwise I would expect some Equinox classes in my stack trace (e.g. 
org.eclipse.osgi.internal.loader.BundleLoader)

Original comment by jebo0...@googlemail.com on 7 Nov 2012 at 1:20

GoogleCodeExporter commented 8 years ago
OK, anyway when reading your reported issue, I'm nit sure we could do 
something. I don't know very well the security / signed package and related 
stuff, it might not be an issue with mockito, but related to policies.

Java offers a policy mechanism that CGLIB is documenting here : 
http://cglib.sourceforge.net/howto.html

JAVA security protects system resources form unauthorized access by untrusted 
code. Code can be identified by signer and code base url (jar or class file) it 
can be local or downloaded from network. Classes generated by CGLIB do not 
exist at configuration and JVM startup time (generated at runtime), but all 
generated classes have the same protection domain (signer and codebase) as 
cglib itself and can be used in WS or by RMI application with security manager. 
To grant permissions for generated classes grant permissions for cglib 
binaries. Default security configuration is in java.policy file. This is 
example policy file, it grants all permissions for cglib and generated code.

        grant codeBase "file:${user.dir}/jars/cglib.jar"{
            permission java.security.AllPermission; 
         };

You can try that, just replace cglib.jar by mockito.jar

Original comment by brice.du...@gmail.com on 7 Nov 2012 at 5:12

GoogleCodeExporter commented 8 years ago
Also if you have any pointer or even pull request to fix the issue, it would be 
very welcome :)

Original comment by brice.du...@gmail.com on 7 Nov 2012 at 6:17

GoogleCodeExporter commented 8 years ago
Linking to the original issue on eclipse : 
https://bugs.eclipse.org/bugs/show_bug.cgi?id=349164

Original comment by brice.du...@gmail.com on 8 Nov 2012 at 9:51

GoogleCodeExporter commented 8 years ago
Thanks for cross-referencing... from my point of view these cases are *similar* 
but they are not the same. The Eclipse bug refers to the situation where a 
signed mockito bundle is used (the one from the Eclipse Orbit project). This 
bug (and my own problem) seems to be caused by creating mocks for a package 
that originates from a signed jar.

But your java.policy file approach seems promising to me. I hope to find some 
time to try this later today.

Thanks.

Original comment by mknaue...@gmail.com on 8 Nov 2012 at 9:55

GoogleCodeExporter commented 8 years ago
Unfortunately it didn't work in our case, and after some digging around I am 
quite confident that the java.policy approach cannot work. The 
SecurityException will be thrown anyway.

What's a bit weird is the fact that we are not seeing the error together with 
Mockito.

In any case, we've changed our build setup now to work around this problem. In 
case we are signing our jar files, we are not running the JUnit tests at all. 
They are run in a non-signing build only. This is not an approach that I'd 
suggest that others should follow, but it helps us to survive the time until we 
have a proper fix and a final solution for this problem.

Original comment by mknaue...@gmail.com on 10 Nov 2012 at 6:35

GoogleCodeExporter commented 8 years ago
So it looks like Mockito is not really working if one wants to mock classes 
that come from signed jars?

I haven't spend great deal of time so I may be wrong. We need to generate the 
proxies to the same package so that it is possible to mock package-protected 
methods.
1. we could improve mockito so that we can generate proxies with a different 
package but make sure mocking still works nicely for package-protected methods. 
I don't know if this is even possible :)
2. we could offer a configuration option / extension point so that the user can 
specify the package of the proxy classes. For example, mock(Foo.class, 
withSettings().fromSignedJar()). However, mocking of package-protected methods 
or classes will not be possible. Some of those limitations may not be 
detectable so we won't be able to improve their error reporting.

Hope that helps!

Original comment by szcze...@gmail.com on 10 Nov 2012 at 6:49

GoogleCodeExporter commented 8 years ago
I found in some descriptions in the web that PowerMock 
(http://code.google.com/p/powermock/) is able to work with signed jar's. Maybe 
it would help to look into their code how they solved this.

Original comment by mknaue...@gmail.com on 10 Nov 2012 at 7:02

GoogleCodeExporter commented 8 years ago
We also deal with signed packages. E.g. we check if the jar is signed and then 
we apply different package naming rule. Apparently, it does not work for your 
scenario.

Can you try using PowerMockito and see if it can work in your environement?

Original comment by szcze...@gmail.com on 10 Nov 2012 at 8:08

GoogleCodeExporter commented 8 years ago
I'm facing the same issue. The tests are actually executed outside of an OSGi 
container using Maven Surefire plug-in (maven-surefire-plugin:2.14.1). It seems 
that I can't spy/mock any class out of a signed jar. Unfortunately, the signed 
jars come from a dependency. Thus, I'm not in a situation to easily work around 
this.

Original comment by eclipseguru@gmail.com on 9 Dec 2013 at 2:45

GoogleCodeExporter commented 8 years ago
FWIW, my stack trace is a litte bit different, though:

org.mockito.cglib.core.CodeGenerationException: 
java.lang.reflect.InvocationTargetException-->null
  at java.lang.ClassLoader.checkCerts(ClassLoader.java:952)
  at java.lang.ClassLoader.preDefineClass(ClassLoader.java:666)
  at java.lang.ClassLoader.defineClass(ClassLoader.java:794)
  at sun.reflect.GeneratedMethodAccessor12.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at org.mockito.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:385)
  at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:220)
  at org.mockito.cglib.reflect.FastClass$Generator.create(FastClass.java:65)
  at org.mockito.cglib.proxy.MethodProxy.helper(MethodProxy.java:121)
  at org.mockito.cglib.proxy.MethodProxy.init(MethodProxy.java:75)
  at org.mockito.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:214)
  at org.mockito.internal.creation.AbstractMockitoMethodProxy.invokeSuper(AbstractMockitoMethodProxy.java:10)
  at org.mockito.internal.invocation.realmethod.CGLIBProxyRealMethod.invoke(CGLIBProxyRealMethod.java:22)
  at org.mockito.internal.invocation.realmethod.FilteredCGLIBProxyRealMethod.invoke(FilteredCGLIBProxyRealMethod.java:27)
  at org.mockito.internal.invocation.Invocation.callRealMethod(Invocation.java:213)
  at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:36)
  at org.mockito.internal.MockHandler.handle(MockHandler.java:101)
  at org.mockito.internal.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:36)
  at org.mockito.internal.creation.MethodInterceptorFilter.intercept(MethodInterceptorFilter.java:48)
  at codegen.my.SpyedClass$$EnhancerByMockitoWithCGLIB$$65d4d85.getSomething(<generated>)
  at my.SpyedClass.getSomething(SpyedClass.java:123)
  ..

Original comment by eclipseguru@gmail.com on 9 Dec 2013 at 2:58

GoogleCodeExporter commented 8 years ago
Hey,

I'm not an expert on signed package, I've never worked with them. However it 
does look similar to this issue : 
http://stackoverflow.com/questions/19358145/cglib-error-after-launching-program-
signer-information-does-not-match-signer-in

Could it be a classpath issue.
Also it the problem is still there, if you could reproduce in a project and 
attach it to the issue.

Original comment by brice.du...@gmail.com on 9 Dec 2013 at 5:45

GoogleCodeExporter commented 8 years ago
Or even better cold you check the MANIFEST of the mockito jar ?

I'm not sure but users have reported similar error with signed CGLIB jars, 
Mockito is not signed, but maybe you are running a different  version of 
mockito.

⇒ https://forums.oracle.com/thread/2472732

Original comment by brice.du...@gmail.com on 9 Dec 2013 at 5:50

GoogleCodeExporter commented 8 years ago
Also if it is a classpath issue, because there's different version of a class, 
take a look at a possible way to troubleshoot the issue ⇒ 
http://stackoverflow.com/a/17565202/48136

Original comment by brice.du...@gmail.com on 9 Dec 2013 at 5:58

GoogleCodeExporter commented 8 years ago
FWIW, there is a very good analysis posted on https://bugs.eclipse.org/349164 
(comment 5). I'll quote the relevant bits below:

> This is how mockito determines the projection domain used later for class
> definition.
> 
> 
> static {
>   PROTECTION_DOMAIN = (ProtectionDomain)AccessController
>     .doPrivileged(new PrivilegedAction() {
>        public Object run() {
>          return ReflectUtils.class.getProtectionDomain();
>        }
>      });
> 
> 
> The class definition takes place here:
> 
> 
> public static Class defineClass(String className, 
>                                 byte[] b,
>                                 ClassLoader loader)
>   throws Exception
> {
>   Object[] args = new Object[]{
>     className,
>     b,
>     new Integer(0),
>     new Integer(b.length),
>     PROTECTION_DOMAIN
>   };
>   return (Class)DEFINE_CLASS.invoke(loader, args);
> }
> 
> DEFINE_CLASS is a Method object that points to the according
> java.lang.ClassLoader.defineClass(...). The latter method calls
> ClassLoader.checkCerts(). A comment in that code states
> 
> // first class in this package gets to define which
> // certificates must be the same for all other classes
> // in this package
> 
> If you debug the example above you will see that the first class loaded in
> the package is the class or the interface that you want to mock. As the
> certificates of the mock are provided by the
> PROTECTION_DOMAIN.getCodeSource().getCertificates() they do not match. This
> throws a SecurityException, which is swallowed. But it leads to the
> misleading exception message mentioned in my description above.
> 
> Not signing mockito would solve the problem described above, since then the
> certificates of both classes would be the same (that is to say none). But
> once you want to mock a class that actually has certificates, I would expect
> that the same problem occurs, since the certificates again would not match.
> 
> Maybe I'm getting this all wrong, since it isn't my domain at all. But I
> think a solution would be to retrieve the protection domain of the class I
> want to mock instead using the static approach.
> 

Original comment by eclipseguru@gmail.com on 15 Apr 2014 at 11:07

GoogleCodeExporter commented 8 years ago
Actually the mentioned code is the one of CGLIB.

But to try a fix we would need a test case for that. Also I'm not sure about 
this either, as I wonder how the generated class (mock) will behave as it will 
use other classes of CGLIB/Mockito.

Original comment by brice.du...@gmail.com on 21 Apr 2014 at 4:51

GoogleCodeExporter commented 8 years ago
Is it allowed to customize code in mockito-repackaged or should the modifcation 
be submitted upstream to CGLIB?

Original comment by gun...@wagenknecht.org on 17 Jul 2014 at 12:09

GoogleCodeExporter commented 8 years ago
FYI:

I submitted a pull request to CGLIB in order to allow Mockito (and other users 
of CGLIB) to use the proper ProtectionDomain when generating classes.

https://github.com/cglib/cglib/pull/15

Feedback is welcome!

Original comment by gun...@wagenknecht.org on 17 Jul 2014 at 8:46

GoogleCodeExporter commented 8 years ago
Hi, the pull request has been merged. Is there a procedure for updating CGLIB 
in Mockito?

Original comment by gun...@wagenknecht.org on 18 Jul 2014 at 4:27