1 // Copyright 2021 Code Intelligence GmbH 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.code_intelligence.jazzer.utils 16 17 import java.lang.IllegalArgumentException 18 19 private val BASE_INCLUDED_CLASS_NAME_GLOBS = listOf( 20 "**", // everything 21 ) 22 23 private val BASE_EXCLUDED_CLASS_NAME_GLOBS = listOf( 24 "\\[**", // array types 25 "com.code_intelligence.jazzer.**", 26 "com.sun.**", // package for Proxy objects 27 "java.**", 28 "javax.**", 29 "jaz.Ter", // safe companion of the honeypot class used by sanitizers 30 "jaz.Zer", // honeypot class used by sanitizers 31 "jdk.**", 32 "kotlin.**", 33 "sun.**", 34 ) 35 36 class ClassNameGlobber(includes: List<String>, excludes: List<String>) { 37 // If no include globs are provided, start with all classes. 38 private val includeMatchers = (if (includes.isEmpty()) BASE_INCLUDED_CLASS_NAME_GLOBS else includes) 39 .map(::SimpleGlobMatcher) 40 41 // If no include globs are provided, additionally exclude stdlib classes as well as our own classes. 42 private val excludeMatchers = (if (includes.isEmpty()) BASE_EXCLUDED_CLASS_NAME_GLOBS + excludes else excludes) 43 .map(::SimpleGlobMatcher) 44 includesnull45 fun includes(className: String): Boolean { 46 return includeMatchers.any { it.matches(className) } && excludeMatchers.none { it.matches(className) } 47 } 48 } 49 50 class SimpleGlobMatcher(val glob: String) { 51 private enum class Type { 52 // foo.bar (matches foo.bar only) 53 FULL_MATCH, 54 // foo.** (matches foo.bar and foo.bar.baz) 55 PATH_WILDCARD_SUFFIX, 56 // foo.* (matches foo.bar, but not foo.bar.baz) 57 SEGMENT_WILDCARD_SUFFIX, 58 } 59 60 private val type: Type 61 private val prefix: String 62 63 init { 64 // Remain compatible with globs such as "\\[" that use escaping. 65 val pattern = glob.replace("\\", "") 66 when { 67 !pattern.contains('*') -> { 68 type = Type.FULL_MATCH 69 prefix = pattern 70 } 71 // Ends with "**" and contains no other '*'. 72 pattern.endsWith("**") && pattern.indexOf('*') == pattern.length - 2 -> { 73 type = Type.PATH_WILDCARD_SUFFIX 74 prefix = pattern.removeSuffix("**") 75 } 76 // Ends with "*" and contains no other '*'. 77 pattern.endsWith('*') && pattern.indexOf('*') == pattern.length - 1 -> { 78 type = Type.SEGMENT_WILDCARD_SUFFIX 79 prefix = pattern.removeSuffix("*") 80 } 81 else -> throw IllegalArgumentException( 82 "Unsupported glob pattern (only foo.bar, foo.* and foo.** are supported): $pattern" 83 ) 84 } 85 } 86 87 /** 88 * Checks whether [maybeInternalClassName], which may be internal (foo/bar) or not (foo.bar), matches [glob]. 89 */ matchesnull90 fun matches(maybeInternalClassName: String): Boolean { 91 val className = maybeInternalClassName.replace('/', '.') 92 return when (type) { 93 Type.FULL_MATCH -> className == prefix 94 Type.PATH_WILDCARD_SUFFIX -> className.startsWith(prefix) 95 Type.SEGMENT_WILDCARD_SUFFIX -> { 96 // className starts with prefix and contains no further '.'. 97 className.startsWith(prefix) && 98 className.indexOf('.', startIndex = prefix.length) == -1 99 } 100 } 101 } 102 } 103