View Javadoc
1   package org.pojomatic;
2   
3   import static org.testng.Assert.*;
4   
5   import java.io.FilePermission;
6   import java.lang.invoke.MethodHandles;
7   import java.lang.reflect.ReflectPermission;
8   import java.net.SocketPermission;
9   import java.security.AccessControlException;
10  import java.security.Permission;
11  import java.security.Policy;
12  import java.security.ProtectionDomain;
13  import java.security.SecurityPermission;
14  import java.util.HashSet;
15  import java.util.Set;
16  
17  import org.pojomatic.annotations.Property;
18  import org.pojomatic.annotations.PropertyFormat;
19  import org.pojomatic.formatter.DefaultEnhancedPropertyFormatter;
20  import org.pojomatic.internal.PojomatorFactoryTest;
21  import org.testng.annotations.Test;
22  
23  import com.google.common.collect.ImmutableSet;
24  
25  public class SecurityTest {
26    private SecurityManager originalSecurityManager;
27    private Policy originalPolicy;
28  
29    private Set<Permission> requestedPermissions = new HashSet<>();
30  
31    private void setPolicy() {
32      originalSecurityManager = System.getSecurityManager();
33      originalPolicy = Policy.getPolicy();
34      final ProtectionDomain testProtectionDomain = PojomatorFactoryTest.class.getProtectionDomain();
35      final ProtectionDomain mainProtectionDomain = Pojomatic.class.getProtectionDomain();
36      Policy.setPolicy(new Policy() {
37  
38        @Override
39        public boolean implies(ProtectionDomain domain, Permission permission) {
40          if (domain == mainProtectionDomain) {
41            requestedPermissions.add(permission);
42            return true;
43          }
44          if (permission instanceof SecurityPermission && "setPolicy".equals(permission.getName())) {
45            return true;
46          }
47          if (permission instanceof RuntimePermission && "setSecurityManager".equals(permission.getName())) {
48            return true;
49          }
50          if (testProtectionDomain.equals(domain)) {
51            return false;
52          }
53          return true; // let TestNG do it's thing.
54        }
55      });
56  
57      System.setSecurityManager(new SecurityManager());
58    }
59  
60    private void restorePolicy() {
61      System.setSecurityManager(originalSecurityManager);
62      Policy.setPolicy(originalPolicy);
63    }
64  
65    private static class Inaccessible{}
66  
67    private static class SimplePojo {
68      @Property
69      int x = 0;
70  
71      @Property
72      int getY() { return 0; }
73  
74      @Property
75      Inaccessible z;
76    }
77  
78    @Test
79    public void testSecurityModel() {
80      requestedPermissions.clear();
81      SimplePojo pojo = new SimplePojo();
82      String toString = null;
83      boolean equals;
84      int hashCode;
85      try {
86        setPolicy();
87        equals = Pojomatic.pojomator(SimplePojo.class).doEquals(new SimplePojo(), new SimplePojo());
88        hashCode = Pojomatic.pojomator(SimplePojo.class).doHashCode(new SimplePojo());
89        toString = Pojomatic.pojomator(SimplePojo.class).doToString(pojo);
90      }
91      finally {
92        restorePolicy();
93      }
94      assertEquals(toString, "SimplePojo{x: {0}, z: {null}, y: {0}}");
95      assertTrue(equals);
96      assertEquals(hashCode, 31*31*31);
97  
98      String testClassPath = SimplePojo.class.getProtectionDomain().getCodeSource().getLocation().getPath();
99      String simplePojoPath = SimplePojo.class.getName().replace('.', '/') + ".class";
100 
101     assertEquals(
102       requestedPermissions,
103       ImmutableSet.of(
104         new FilePermission(testClassPath + simplePojoPath, "read"),
105         new RuntimePermission(haveLookupDefineClass() ? "defineClass" : "accessDeclaredMembers"),
106         new ReflectPermission("suppressAccessChecks")));
107   }
108 
109   public static class AttackingConstructorFormatter extends DefaultEnhancedPropertyFormatter {
110     {
111       System.getSecurityManager().checkListen(80); // should fail
112     }
113   }
114 
115   private static class AttackingConstructorFormattedPojo {
116     @Property
117     @PropertyFormat(SecurityTest.AttackingConstructorFormatter.class)
118     int x = 0;
119   }
120 
121 
122   /**
123    * Verify that constructor code for property formatters is not running with Pojomatic security privileges
124    */
125   @Test
126   public void testPropertyFormatterConstructorSecurity() {
127     try {
128       setPolicy();
129       Pojomatic.pojomator(AttackingConstructorFormattedPojo.class);
130       fail("Exception expected");
131     }
132     catch (AccessControlException e) {
133       assertTrue(e.getPermission() instanceof SocketPermission);
134     }
135     finally {
136       restorePolicy();
137     }
138   }
139 
140   public static class AttackingStaticInitializerFormatter extends DefaultEnhancedPropertyFormatter {
141     static {
142       System.getSecurityManager().checkListen(80); // should fail
143     }
144   }
145 
146   private static class AttackingStaticInitializerFormattedPojo {
147     @Property
148     @PropertyFormat(SecurityTest.AttackingStaticInitializerFormatter.class)
149     int x = 0;
150   }
151 
152   /**
153    * Verify that constructor code for property formatters is not running with Pojomatic security privileges
154    */
155   @Test
156   public void testPropertyFormatterStaticInitializerSecurity() {
157     try {
158       setPolicy();
159       Pojomatic.pojomator(AttackingStaticInitializerFormattedPojo.class);
160       fail("Exception expected");
161     }
162     catch (ExceptionInInitializerError e) {
163       assertEquals(e.getCause().getClass(), AccessControlException.class);
164       assertTrue(((AccessControlException) e.getCause()).getPermission() instanceof SocketPermission);
165     }
166     finally {
167       restorePolicy();
168     }
169   }
170 
171   private static boolean haveLookupDefineClass() {
172     try {
173       MethodHandles.Lookup.class.getMethod("defineClass", new Class<?>[] { byte[].class });
174       return true;
175     }
176     catch (Throwable t) {
177       return false;
178     }
179   }
180 }