Skip to content

Commit 722c9c5

Browse files
authored
Support metadata filtering (#1568)
* Support metadata filtering * Provide MDG filter files * Fix issue of missing file * Fix uri problem * Whitelist everything if no whitelist file is provided
1 parent 7bbf538 commit 722c9c5

31 files changed

+584
-116
lines changed

test-app/app/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,11 @@ task collectAllJars {
734734
}
735735
}
736736

737+
task copyMetadataFilters(type: Copy) {
738+
from "$rootDir/whitelist.mdg", "$rootDir/blacklist.mdg"
739+
into "$BUILD_TOOLS_PATH"
740+
}
741+
737742
task copyMetadata {
738743
doLast {
739744
copy {
@@ -748,6 +753,8 @@ task buildMetadata(type: BuildToolTask) {
748753
dependsOn ':android-metadata-generator:jar'
749754
}
750755

756+
dependsOn copyMetadataFilters
757+
751758
// As some external gradle plugins can reorder the execution order of the tasks it may happen that buildMetadata is executed after merge{Debug/Release}Assets
752759
// in that case the metadata won't be included in the result apk and it will crash, so to avoid this we are adding the copyMetadata task which will manually copy
753760
// the metadata files in the merge assets folder and they will be added to the result apk
@@ -924,6 +931,8 @@ task cleanSbg(type: Delete) {
924931

925932
task cleanMdg(type: Delete) {
926933
delete "$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR",
934+
"$BUILD_TOOLS_PATH/whitelist.mdg",
935+
"$BUILD_TOOLS_PATH/blacklist.mdg",
927936
"$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES",
928937
"$METADATA_OUT_PATH"
929938
}

test-app/build-tools/android-metadata-generator/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ dependencies {
4848
compile 'com.google.code.gson:gson:2.8.5'
4949
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-metadata-jvm', version: '0.1.0'
5050
compile files("./src/libs/dx.jar")
51+
52+
testCompile 'junit:junit:4.13'
53+
testCompile 'org.mockito:mockito-core:3.0.0'
54+
implementation 'junit:junit:4.12'
5155
}
5256

5357
task copyNecessaryFiles {

test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Builder.java

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import com.telerik.metadata.parsing.kotlin.extensions.bytecode.BytecodeExtensionFunctionsCollector;
1818
import com.telerik.metadata.parsing.kotlin.metadata.ClassMetadataParser;
1919
import com.telerik.metadata.parsing.kotlin.metadata.bytecode.BytecodeClassMetadataParser;
20+
import com.telerik.metadata.security.MetadataSecurityViolationException;
21+
import com.telerik.metadata.security.classes.SecuredClassRepository;
22+
import com.telerik.metadata.security.classes.SecuredNativeClassDescriptor;
2023
import com.telerik.metadata.storage.functions.FunctionsStorage;
2124
import com.telerik.metadata.storage.functions.extensions.ExtensionFunctionsStorage;
2225

@@ -48,26 +51,26 @@ static TreeNode build(List<String> paths) throws Exception {
4851
if (file.isFile()) {
4952
if (path.endsWith(".jar")) {
5053
JarFile jar = JarFile.readJar(path);
51-
ClassRepo.addToCache(jar);
54+
SecuredClassRepository.INSTANCE.addToCache(jar);
5255
}
5356
} else if (file.isDirectory()) {
5457
ClassDirectory dir = ClassDirectory.readDirectory(path);
55-
ClassRepo.addToCache(dir);
58+
SecuredClassRepository.INSTANCE.addToCache(dir);
5659
}
5760
}
5861
}
5962

6063
TreeNode root = TreeNode.getRoot();
6164

62-
String[] classNames = ClassRepo.getClassNames();
65+
String[] classNames = SecuredClassRepository.INSTANCE.getClassNames();
6366

6467
for (String className : classNames) {
6568
try {
66-
NativeClassDescriptor clazz = ClassRepo.findClass(className);
67-
if (clazz == null) {
68-
throw new ClassNotFoundException("Class " + className + " not found in the input android libraries.");
69+
SecuredNativeClassDescriptor clazz = SecuredClassRepository.INSTANCE.findClass(className);
70+
if (!clazz.isUsageAllowed()) {
71+
throwMetadataSecurityViolationException(className);
6972
} else {
70-
tryCollectKotlinExtensionFunctions(clazz);
73+
tryCollectKotlinExtensionFunctions(clazz.getNativeDescriptor());
7174
}
7275
} catch (Throwable e) {
7376
System.out.println("Skip " + className);
@@ -86,11 +89,11 @@ static TreeNode build(List<String> paths) throws Exception {
8689
// our class path API 17
8790
// Class<?> clazz = Class.forName(className, false, loader);
8891

89-
NativeClassDescriptor clazz = ClassRepo.findClass(className);
90-
if (clazz == null) {
91-
throw new ClassNotFoundException("Class " + className + " not found in the input android libraries.");
92+
SecuredNativeClassDescriptor clazz = SecuredClassRepository.INSTANCE.findClass(className);
93+
if (!clazz.isUsageAllowed()) {
94+
throwMetadataSecurityViolationException(className);
9295
} else {
93-
generate(clazz, root);
96+
generate(clazz.getNativeDescriptor(), root);
9497
}
9598
} catch (Throwable e) {
9699
System.out.println("Skip " + className);
@@ -103,6 +106,10 @@ static TreeNode build(List<String> paths) throws Exception {
103106
return root;
104107
}
105108

109+
private static void throwMetadataSecurityViolationException(String className){
110+
throw new MetadataSecurityViolationException("Class " + className + " could not be used!");
111+
}
112+
106113
private static void tryCollectKotlinExtensionFunctions(NativeClassDescriptor classDescriptor) {
107114
if (classDescriptor instanceof KotlinClassDescriptor) {
108115
ExtensionFunctionsCollector extensionFunctionsCollector = new BytecodeExtensionFunctionsCollector(new BytecodeClassMetadataParser());
@@ -319,8 +326,10 @@ private static void getFieldsFromImplementedInterfaces(NativeClassDescriptor cla
319326
String[] implementedInterfacesNames = clazz.getInterfaceNames();
320327
if (implementedInterfacesNames.length > 0) {
321328
for (String currInterface : implementedInterfacesNames) {
322-
interfaceClass = ClassRepo.findClass(currInterface);
323-
if (interfaceClass != null) {
329+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findClass(currInterface);
330+
331+
if (securedNativeClassDescriptor.isUsageAllowed()) {
332+
interfaceClass = securedNativeClassDescriptor.getNativeDescriptor();
324333
fields = interfaceClass.getFields();
325334

326335
// If the interface iteself extends other interfaces - add their fields too
@@ -361,14 +370,14 @@ private static TreeNode getOrCreateNode(TreeNode root, NativeTypeDescriptor type
361370
node = createArrayNode(root, typeName);
362371
} else {
363372
String name = ClassUtil.getCanonicalName(type.getSignature());
364-
NativeClassDescriptor clazz = ClassRepo.findClass(name);
373+
SecuredNativeClassDescriptor clazz = SecuredClassRepository.INSTANCE.findNearestAllowedClass(name);
365374

366375
// if clazz is not found in the ClassRepo, the method/field being analyzed will be skipped
367-
if (clazz == null) {
376+
if (!clazz.isUsageAllowed()) {
368377
return null;
369378
}
370379

371-
node = getOrCreateNode(root, clazz, null);
380+
node = getOrCreateNode(root, clazz.getNativeDescriptor(), null);
372381
}
373382

374383
return node;
@@ -451,10 +460,11 @@ private static TreeNode getOrCreateNode(TreeNode root, NativeClassDescriptor cla
451460
if (node.baseClassNode == null) {
452461
NativeClassDescriptor baseClass = null;
453462
if (predefinedSuperClassname != null) {
454-
baseClass = ClassUtil.getClassByName(predefinedSuperClassname);
463+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findNearestAllowedClass(predefinedSuperClassname);
464+
baseClass = securedNativeClassDescriptor.isUsageAllowed() ? securedNativeClassDescriptor.getNativeDescriptor() : null;
455465
} else {
456466
baseClass = clazz.isInterface()
457-
? ClassUtil.getClassByName("java.lang.Object")
467+
? SecuredClassRepository.INSTANCE.findClass("java.lang.Object").getNativeDescriptor() // java.lang.Object should always be available
458468
: ClassUtil.getSuperclass(clazz);
459469
}
460470
if (baseClass != null) {
@@ -505,13 +515,17 @@ private static TreeNode createArrayNode(TreeNode root, String className)
505515
child.nodeType = node.nodeType;
506516
child.arrayElement = node;
507517
} else {
508-
NativeClassDescriptor clazz = ClassRepo.findClass(name);
509-
child.nodeType = clazz.isInterface() ? TreeNode.Interface
510-
: TreeNode.Class;
511-
if (clazz.isStatic()) {
512-
child.nodeType |= TreeNode.Static;
518+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findNearestAllowedClass(name);
519+
if (securedNativeClassDescriptor.isUsageAllowed()) {
520+
NativeClassDescriptor nativeClassDescriptor = securedNativeClassDescriptor.getNativeDescriptor();
521+
child.nodeType = nativeClassDescriptor.isInterface() ? TreeNode.Interface
522+
: TreeNode.Class;
523+
if (nativeClassDescriptor.isStatic()) {
524+
child.nodeType |= TreeNode.Static;
525+
}
526+
child.arrayElement = getOrCreateNode(root, nativeClassDescriptor, null);
513527
}
514-
child.arrayElement = getOrCreateNode(root, clazz, null);
528+
515529
}
516530
}
517531

test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/ClassRepo.java

Lines changed: 0 additions & 41 deletions
This file was deleted.

test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/ClassUtil.java

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,53 @@
33
import com.telerik.metadata.parsing.NativeClassDescriptor;
44
import com.telerik.metadata.parsing.NativeMethodDescriptor;
55
import com.telerik.metadata.parsing.NativeTypeDescriptor;
6+
import com.telerik.metadata.security.classes.SecuredClassRepository;
7+
import com.telerik.metadata.security.classes.SecuredNativeClassDescriptor;
68

79
import java.util.ArrayList;
810

911
public class ClassUtil {
1012
private ClassUtil() {
1113
}
1214

13-
public static boolean isPrimitive(NativeClassDescriptor clazz) {
14-
boolean isPrimitive = !clazz.isClass() && !clazz.isEnum()
15-
&& !clazz.isInterface();
16-
return isPrimitive;
15+
static boolean isPrimitive(NativeClassDescriptor clazz) {
16+
return !clazz.isClass() && !clazz.isEnum()
17+
&& !clazz.isInterface();
1718
}
1819

19-
public static boolean isPrimitive(NativeTypeDescriptor type) {
20-
boolean isPrimitive = type.equals(NativeTypeDescriptor.Companion.getBOOLEAN())
21-
|| type.equals(NativeTypeDescriptor.Companion.getCHAR()) || type.equals(NativeTypeDescriptor.Companion.getBYTE())
22-
|| type.equals(NativeTypeDescriptor.Companion.getSHORT()) || type.equals(NativeTypeDescriptor.Companion.getINT())
23-
|| type.equals(NativeTypeDescriptor.Companion.getLONG()) || type.equals(NativeTypeDescriptor.Companion.getFLOAT())
24-
|| type.equals(NativeTypeDescriptor.Companion.getDOUBLE()) || type.equals(NativeTypeDescriptor.Companion.getVOID());
20+
static boolean isPrimitive(NativeTypeDescriptor type) {
2521

26-
return isPrimitive;
22+
return type.equals(NativeTypeDescriptor.Companion.getBOOLEAN())
23+
|| type.equals(NativeTypeDescriptor.Companion.getCHAR()) || type.equals(NativeTypeDescriptor.Companion.getBYTE())
24+
|| type.equals(NativeTypeDescriptor.Companion.getSHORT()) || type.equals(NativeTypeDescriptor.Companion.getINT())
25+
|| type.equals(NativeTypeDescriptor.Companion.getLONG()) || type.equals(NativeTypeDescriptor.Companion.getFLOAT())
26+
|| type.equals(NativeTypeDescriptor.Companion.getDOUBLE()) || type.equals(NativeTypeDescriptor.Companion.getVOID());
2727
}
2828

29-
public static boolean isPrimitive(String name) {
30-
boolean isPrimitive = name.equals("C") || name.equals("Z")
31-
|| name.equals("B") || name.equals("S") || name.equals("I")
32-
|| name.equals("J") || name.equals("F") || name.equals("D")
33-
|| name.equals("V");
34-
return isPrimitive;
29+
static boolean isPrimitive(String name) {
30+
return name.equals("C") || name.equals("Z")
31+
|| name.equals("B") || name.equals("S") || name.equals("I")
32+
|| name.equals("J") || name.equals("F") || name.equals("D")
33+
|| name.equals("V");
3534
}
3635

37-
public static boolean isArray(NativeClassDescriptor clazz) {
38-
boolean isArray = isArray(clazz.getClassName());
39-
return isArray;
36+
static boolean isArray(NativeClassDescriptor clazz) {
37+
return isArray(clazz.getClassName());
4038
}
4139

42-
public static boolean isArray(String className) {
43-
boolean isArray = className.startsWith("[");
44-
return isArray;
40+
static boolean isArray(String className) {
41+
return className.startsWith("[");
4542
}
4643

47-
public static NativeClassDescriptor getEnclosingClass(NativeClassDescriptor clazz) {
44+
static NativeClassDescriptor getEnclosingClass(NativeClassDescriptor clazz) {
4845
NativeClassDescriptor enclosingClass = null;
4946

5047
String className = clazz.getClassName();
5148
int idx = className.lastIndexOf("$");
5249
if (idx > 0) {
5350
String name = className.substring(0, idx);
54-
enclosingClass = ClassRepo.findClass(name);
51+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findClass(name);
52+
enclosingClass = securedNativeClassDescriptor.isUsageAllowed() ? securedNativeClassDescriptor.getNativeDescriptor() : null;
5553
}
5654

5755
return enclosingClass;
@@ -67,7 +65,7 @@ public static String getSimpleName(NativeClassDescriptor clazz) {
6765
return simpleName;
6866
}
6967

70-
public static NativeMethodDescriptor[] getAllMethods(NativeClassDescriptor clazz) {
68+
static NativeMethodDescriptor[] getAllMethods(NativeClassDescriptor clazz) {
7169
ArrayList<NativeMethodDescriptor> methods = new ArrayList<NativeMethodDescriptor>();
7270
NativeClassDescriptor currentClass = clazz;
7371
while (currentClass != null) {
@@ -86,25 +84,21 @@ public static String getCanonicalName(String className) {
8684
String canonicalName;
8785
if (className.startsWith("L") && className.endsWith(";")) {
8886
canonicalName = className.substring(1, className.length() - 1)
89-
.replace('/', '.');
87+
.replace('/', '.');
9088
} else {
9189
canonicalName = className;
9290
}
9391
return canonicalName;
9492
}
9593

96-
public static NativeClassDescriptor getSuperclass(NativeClassDescriptor clazz) {
94+
static NativeClassDescriptor getSuperclass(NativeClassDescriptor clazz) {
9795
NativeClassDescriptor superClass = null;
9896
if (!clazz.getClassName().equals("java.lang.Object")) {
9997
String superClassName = clazz.getSuperclassName();
100-
superClass = ClassRepo.findClass(superClassName);
98+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findNearestAllowedClass(superClassName);
99+
superClass = securedNativeClassDescriptor.isUsageAllowed() ? securedNativeClassDescriptor.getNativeDescriptor() : null;
101100
}
102101
return superClass;
103102
}
104103

105-
public static NativeClassDescriptor getClassByName(String className) {
106-
NativeClassDescriptor clazz = ClassRepo.findClass(className);
107-
return clazz;
108-
}
109-
110104
}

test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/Generator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.telerik.metadata;
22

33
import com.telerik.metadata.analytics.AnalyticsConfiguration;
4+
import com.telerik.metadata.security.filtering.input.user.UserPatternsCollection;
45

56
import java.io.BufferedReader;
67
import java.io.File;
@@ -17,12 +18,16 @@ public class Generator {
1718
private static final String ANALYTICS_ARGUMENT_BEGINNING = "analyticsFilePath=";
1819
private static final String MDG_OUTPUT_DIR = "mdg-output-dir.txt";
1920
private static final String MDG_JAVA_DEPENDENCIES = "mdg-java-dependencies.txt";
21+
private static final String MDG_WHITELIST = "whitelist.mdg";
22+
private static final String MDG_BLACKLIST = "blacklist.mdg";
2023

2124
/**
2225
* @param args
2326
*/
2427
public static void main(String[] args) {
2528
enableAnalyticsBasedOnArgs(args);
29+
UserPatternsCollection.INSTANCE.populateWhitelistEntriesFromFile(MDG_WHITELIST);
30+
UserPatternsCollection.INSTANCE.populateBlacklistEntriesFromFile(MDG_BLACKLIST);
2631

2732
try {
2833
String metadataOutputDir;

test-app/build-tools/android-metadata-generator/src/src/com/telerik/metadata/parsing/ClassParser.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.telerik.metadata.parsing;
22

3-
import com.telerik.metadata.ClassRepo;
3+
import com.telerik.metadata.security.classes.SecuredClassRepository;
4+
import com.telerik.metadata.security.classes.SecuredNativeClassDescriptor;
45

56
import java.util.HashSet;
67
import java.util.Set;
@@ -25,13 +26,15 @@ private HashSet<NativeMethodDescriptor> getAllDefaultMethodsFromImplementedInter
2526
String[] implementedInterfacesNames = clazz.getInterfaceNames();
2627

2728
for (String implementedInterfaceName : implementedInterfacesNames) {
28-
NativeClassDescriptor interfaceClass = ClassRepo.findClass(implementedInterfaceName);
29+
SecuredNativeClassDescriptor securedNativeClassDescriptor = SecuredClassRepository.INSTANCE.findClass(implementedInterfaceName);
2930

30-
if (interfaceClass == null) {
31+
if (!securedNativeClassDescriptor.isUsageAllowed()) {
3132
System.out.println(String.format("WARNING: Skipping interface %s implemented in %s as it cannot be resolved", implementedInterfaceName, clazz.getClassName()));
3233
continue;
3334
}
3435

36+
NativeClassDescriptor interfaceClass = securedNativeClassDescriptor.getNativeDescriptor();
37+
3538
for (NativeMethodDescriptor md : interfaceClass.getMethods()) {
3639
if (!md.isStatic() && !md.isAbstract()) {
3740
collectedDefaultMethods.add(md);

0 commit comments

Comments
 (0)