Skip to content

Commit f07b6af

Browse files
authored
Merge pull request #6 from pixee/add-expanded-classload-sig
Added new signature for protecting `Class.forName(s, bool, cl)`
2 parents 3f39783 + ace8ad2 commit f07b6af

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

.github/badges/jacoco.svg

Lines changed: 1 addition & 1 deletion
Loading

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ In Maven:
3535
<dependency>
3636
<groupId>io.github.pixee</groupId>
3737
<artifactId>java-security-toolkit</artifactId>
38-
<version>1.0.6</version>
38+
<version>1.0.7</version>
3939
</dependency>
4040
```
4141
In Gradle:
4242
```kotlin
43-
implementation("io.github.pixee:java-security-toolkit:1.0.6")
43+
implementation("io.github.pixee:java-security-toolkit:1.0.7")
4444
```
4545

46-
## Contributing
46+
## Contributing
4747
We'd love to get contributions! See [CONTRIBUTING.md](CONTRIBUTING.md).
4848

4949
### Building

src/main/java/io/github/pixee/security/Reflection.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,25 @@ public static Class<?> loadAndVerify(final String name) throws ClassNotFoundExce
6161
return loadAndVerify(name, defaultRestrictions());
6262
}
6363

64+
/**
65+
* This method sandboxes the classloading to prevent possibly dangerous types from being loaded,
66+
* using the default restrictions.
67+
*
68+
* @param name the name of the type to load
69+
* @param initialize whether to initialize the class, passed to {@link Class#forName(String,
70+
* boolean, ClassLoader)}
71+
* @param loader the ClassLoader to use, passed to {@link Class#forName(String, boolean,
72+
* ClassLoader)}
73+
* @throws ClassNotFoundException if the class is not found
74+
* @return the result of {@link Class#forName(String)}, if it passes the default restrictions
75+
*/
76+
public static Class<?> loadAndVerify(
77+
final String name, final boolean initialize, final ClassLoader loader)
78+
throws ClassNotFoundException {
79+
return loadAndVerify(
80+
name, defaultRestrictions(), () -> Class.forName(name, initialize, loader));
81+
}
82+
6483
/**
6584
* This method sandboxes the classloading to prevent possibly dangerous types from being loaded.
6685
*
@@ -72,6 +91,14 @@ public static Class<?> loadAndVerify(final String name) throws ClassNotFoundExce
7291
public static Class<?> loadAndVerify(
7392
final String name, final Set<ReflectionRestrictions> restrictions)
7493
throws ClassNotFoundException {
94+
return loadAndVerify(name, restrictions, () -> Class.forName(name));
95+
}
96+
97+
private static Class<?> loadAndVerify(
98+
final String name,
99+
final Set<ReflectionRestrictions> restrictions,
100+
final ClassSupplier classSupplier)
101+
throws ClassNotFoundException {
75102

76103
// we can do this check up front before we even load the type
77104
if (restrictions.contains(ReflectionRestrictions.MUST_NOT_INVOLVE_CODE_EXECUTION)) {
@@ -83,7 +110,7 @@ public static Class<?> loadAndVerify(
83110
}
84111

85112
// load the type so we can do the other checks
86-
final Class<?> type = Class.forName(name);
113+
final Class<?> type = classSupplier.get();
87114

88115
if (restrictions.contains(ReflectionRestrictions.MUST_BE_PUBLIC)) {
89116
final int modifiers = type.getModifiers();
@@ -116,5 +143,9 @@ public static Class<?> loadAndVerify(
116143
"groovy.",
117144
"org.python.");
118145

146+
private interface ClassSupplier {
147+
Class<?> get() throws ClassNotFoundException;
148+
}
149+
119150
private static final String typeNotAllowedMessage = "type not allowed";
120151
}

src/test/java/io/github/pixee/security/ReflectionTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.junit.jupiter.params.ParameterizedTest;
1313
import org.junit.jupiter.params.provider.ValueSource;
1414

15+
/** Test the protections of {@link Reflection}. */
1516
final class ReflectionTest {
1617

1718
@ParameterizedTest
@@ -26,6 +27,11 @@ void it_protects_dangerous_reflection(final String type) {
2627

2728
// run the same test and confirm that the defaultRestrictions() returns this
2829
assertThrows(SecurityException.class, () -> Reflection.loadAndVerify(type));
30+
31+
// run the same test again on the other signature
32+
assertThrows(
33+
SecurityException.class,
34+
() -> Reflection.loadAndVerify(type, true, getClass().getClassLoader()));
2935
}
3036

3137
@Test
@@ -38,7 +44,10 @@ void it_enforces_public_restriction() throws ClassNotFoundException {
3844
Reflection.loadAndVerify(
3945
ReflectionTest.class.getName(), setOf(ReflectionRestrictions.MUST_BE_PUBLIC)));
4046

41-
// the type we're testing is public and so we should be able to load it
47+
// the type we're testing is public, and so we should be able to load it
48+
Reflection.loadAndVerify(
49+
Reflection.class.getName(), setOf(ReflectionRestrictions.MUST_BE_PUBLIC));
50+
4251
Reflection.loadAndVerify(
4352
Reflection.class.getName(), setOf(ReflectionRestrictions.MUST_BE_PUBLIC));
4453
}
@@ -53,6 +62,8 @@ void it_enforces_public_restriction() throws ClassNotFoundException {
5362
void it_loads_normal_classes(final String typeName) throws ClassNotFoundException {
5463
Class<?> type = Reflection.loadAndVerify(typeName);
5564
assertThat(type, is(not(nullValue())));
65+
type = Reflection.loadAndVerify(typeName, true, getClass().getClassLoader());
66+
assertThat(type, is(not(nullValue())));
5667
}
5768

5869
@Test

0 commit comments

Comments
 (0)