├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── examples ├── custom-message-digest.json ├── deserialization-usage.json ├── file-creation.json ├── file-upload.json ├── http-request-headers.json ├── http-servlet.json ├── jax-rs-endpoint.json ├── jax-ws-endpoint.json ├── response-redirect.json ├── rules-all.json ├── spring-mvc-endpoint.json ├── struts1-endpoint.json └── x509trust-manager-impl.json ├── images ├── graph.png └── report_image.png ├── pom.xml └── src ├── main ├── java │ └── net │ │ └── nandgr │ │ └── cba │ │ ├── App.java │ │ ├── ArchiveAnalyzerCallable.java │ │ ├── ArchiveWalker.java │ │ ├── ByteCodeAnalyzer.java │ │ ├── CustomByteCodeAnalyzer.java │ │ ├── callgraph │ │ ├── graph │ │ │ ├── InvocationGraph.java │ │ │ └── InvocationGraphImpl.java │ │ ├── model │ │ │ ├── Call.java │ │ │ ├── GraphHolder.java │ │ │ └── MethodGraph.java │ │ └── runnable │ │ │ └── ClassCallGraph.java │ │ ├── cli │ │ ├── CliArguments.java │ │ └── CliHelper.java │ │ ├── custom │ │ ├── model │ │ │ ├── Annotation.java │ │ │ ├── Field.java │ │ │ ├── Invocation.java │ │ │ ├── Method.java │ │ │ ├── Parameter.java │ │ │ ├── Rule.java │ │ │ ├── Rules.java │ │ │ └── Variable.java │ │ └── visitor │ │ │ ├── CustomClassAnnotationVisitor.java │ │ │ ├── CustomClassInterfacesVisitor.java │ │ │ ├── CustomClassSuperClassVisitor.java │ │ │ ├── CustomFieldVisitor.java │ │ │ ├── CustomInvocationFinderInsnVisitor.java │ │ │ ├── CustomMethodInvocationVisitor.java │ │ │ ├── CustomMethodVisitor.java │ │ │ ├── RuleVisitorsAnalyzer.java │ │ │ ├── base │ │ │ ├── CustomAbstractClassVisitor.java │ │ │ ├── CustomAbstractMethodInsnVisitor.java │ │ │ ├── CustomAbstractVisitor.java │ │ │ └── CustomVisitor.java │ │ │ └── helper │ │ │ ├── RuleHelper.java │ │ │ └── StringsHelper.java │ │ ├── decompile │ │ ├── Decompiler.java │ │ └── ZipEntryDecompiler.java │ │ ├── exception │ │ ├── BadArgumentsException.java │ │ └── BadRulesException.java │ │ ├── logging │ │ └── LogHelper.java │ │ ├── report │ │ ├── ReportBuilder.java │ │ └── ReportItem.java │ │ └── visitor │ │ └── checks │ │ ├── CustomDeserializationCheck.java │ │ ├── DeserializationCheck.java │ │ ├── InvokeMethodCheck.java │ │ └── util │ │ └── SerializationHelper.java └── resources │ ├── logback.xml │ └── template │ └── report_template.html └── test ├── java └── rules │ ├── AbstractTest.java │ ├── AnnotationsTest.java │ ├── FieldsTest.java │ ├── InterfacesTest.java │ ├── InvocationsTest.java │ ├── MethodsTest.java │ ├── SuperClassTest.java │ └── testfiles │ ├── annotations │ └── AnnotationsTestFile.java │ ├── fields │ └── FieldsTestFile.java │ ├── interfaces │ └── InterfacesTestFile.java │ ├── invocations │ └── InvocationsTestFile.java │ ├── methods │ └── MethodsTestFile.java │ └── superclass │ └── SuperClassTestFile.java └── resources └── rulejson ├── AnnotationsTest.json ├── FieldsTest.json ├── InterfacesTest.json ├── InvocationsTest.json ├── MethodsTest.json └── SuperClassTest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # MVN target folder 2 | target/ 3 | 4 | # Intellij 5 | *.iml 6 | .idea/* 7 | 8 | # logs 9 | *.log 10 | 11 | # app specific 12 | report/ 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | sudo: false 5 | script: mvn clean verify -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # custom-bytecode-analyzer 2 | 3 | Java bytecode analyzer customizable via JSON rules. It is a command-line tool that receives a path containing one or more [Jar](https://en.wikipedia.org/wiki/JAR_(file_format)) or [War](https://en.wikipedia.org/wiki/WAR_(file_format)) files, analyzes them using the provided rules and generates HTML reports with the results. 4 | 5 | [![Build Status](https://travis-ci.org/fergarrui/custom-bytecode-analyzer.svg?branch=master)](https://travis-ci.org/fergarrui/custom-bytecode-analyzer) 6 | 7 | ## Usage 8 | 9 | ``` 10 | usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE 11 | -a,--analyze Path of the directory to run the 12 | analysis. 13 | -c,--checks Space separated list of custom checks 14 | that are going to be run in the analysis. 15 | -f,--custom-file Specify a file in JSON format to run 16 | custom rules. Read more in 17 | https://github.com/fergarrui/custom-bytecode-analyzer. 18 | -h,--help Print this message. 19 | -i,--items-report Max number of items per report. If the 20 | number of issues found exceeds this 21 | value, the report will be split into 22 | different files. Useful if expecting too 23 | many issues in the report. Default: 50. 24 | -o,--output Directory to save the report. Warning - 25 | if there are already saved reports in 26 | this directory they will be overwritten. 27 | Default is "report". 28 | -v,--verbose-debug Increase verbosity to debug mode. 29 | -vv,--verbose-trace Increase verbosity to trace mode - makes it slower, use it only when you need. 30 | ``` 31 | 32 | ## Custom JSON rules 33 | 34 | Rules file can be specified using ```-f,--custom-file``` argument . The file is in JSON format and has the following structure: 35 | 36 | * rules : array(rule) 37 | * name : string 38 | * fields : array(field) 39 | * visibility : (public|protected|private) 40 | * type : string 41 | * valueRegex : string (java regular expression) - only supported if the field is ```final``` 42 | * nameRegex : string (java regular expression) 43 | * report : boolean (default: true) 44 | * interfaces : array(string) 45 | * superClass : string 46 | * annotations : array(annotation) 47 | * type : string 48 | * report : boolean (default: true) 49 | * methods : array(method) 50 | * name : string 51 | * visibility : (public|protected|private) 52 | * parameters : array(parameter) 53 | * type : string 54 | * report : boolean (default: true) 55 | * annotations : array(annotation) 56 | * type : string 57 | * report : boolean (default: true) 58 | * variables : array(variable) 59 | * type : string 60 | * nameRegex : string (java regular expression) 61 | * annotations : array(annotation) 62 | * type : string 63 | * report : boolean (default: true) 64 | * report (default: true) 65 | * annotations : array(annotation) 66 | * type : string 67 | * report : boolean (default: true) 68 | * report : boolean (default: true) 69 | * invocations : array(invocation) 70 | * owner : string 71 | * method : method 72 | * name : string 73 | * visibility : (public|protected|private) 74 | * from : method 75 | * name : string 76 | * visibility : (public|protected|private) 77 | * notFrom : method 78 | * name : string 79 | * visibility : (public|protected|private) 80 | * report : boolean (default:true) 81 | 82 | You can also check ```net.nandgr.cba.custom.model.Rules.java``` to see the structure in Java code. 83 | 84 | ### Examples 85 | 86 | There are already several rules under the directory [examples](https://github.com/fergarrui/custom-bytecode-analyzer/tree/master/examples) . 87 | Anyway, below are listed examples for every rule. 88 | 89 | #### Find custom deserialization 90 | If we need to find classes with custom deserialization, we can do it quite easily. A class defines custom deserialization by implementing ```private void readObject(ObjectInputStream in)```. So we only need to find all classes where that method is defined. It would be enough just to define a rule as: 91 | 92 | ```json 93 | { 94 | "rules": [{ 95 | "name": "Custom deserialization", 96 | "methods": [{ 97 | "name": "readObject", 98 | "visibility": "private", 99 | "parameters" : [{ 100 | "type" : "java.io.ObjectInputStream" 101 | }] 102 | }] 103 | }] 104 | } 105 | ``` 106 | 107 | It will report methods with ```private``` visibility, ```readObject``` as name and a parameter of type ```java.io.ObjectOutputStream```. Parameters are an array, if more than one is specified, all of them have to match to be reported. Since we only have one rule, a report named: custom-deserialization-0.html will be created. 108 | 109 | #### Find custom serialization and deserialization 110 | 111 | In this case, one rule with two methods have to be defined. The same one than in the previous example for deserialization, and a new one to match ```private void writeObject(ObjectOutputStream out)```. As shown in the JSON structure above, the property rules.rule.methods is an array of methods, so a rule like this can be written: 112 | 113 | ```json 114 | { 115 | "rules": [{ 116 | "name": "Custom serialization and deserialization", 117 | "methods": [{ 118 | "name": "readObject", 119 | "visibility": "private", 120 | "parameters" : [{ 121 | "type" : "java.io.ObjectInputStream" 122 | }] 123 | },{ 124 | "name": "writeObject", 125 | "report": "false", 126 | "visibility": "private", 127 | "parameters" : [{ 128 | "type" : "java.io.ObjectOutputStream" 129 | }] 130 | }] 131 | }] 132 | } 133 | ``` 134 | 135 | The property ```report``` was set to false to avoid reporting twice for the same rule. We are using the second method just as a condition, but reporting only ```readObject``` methods should be enough for the purpose of this rule. 136 | 137 | #### Find all method definitions 138 | If a property is not defined, it will always match as true. For example, this rule would return all methods definitions: 139 | ```json 140 | { 141 | "rules": [{ 142 | "name": "Method definitions", 143 | "methods": [{ 144 | }] 145 | }] 146 | } 147 | ``` 148 | 149 | #### Find String.equals method invocations 150 | 151 | Method invocations can also be found. The JSON in this case would be: 152 | ```json 153 | { 154 | "rules": [{ 155 | "name": "String equals", 156 | "invocations": [{ 157 | "owner": "java.lang.String", 158 | "method": { 159 | "name": "equals" 160 | } 161 | }] 162 | }] 163 | } 164 | ``` 165 | The property ```owner``` specifies the class containing the method. 166 | 167 | #### Reflection method invoke 168 | 169 | Another method invocation example a bit more useful than the previous one: 170 | ```json 171 | { 172 | "rules": [{ 173 | "name": "Method invocation by reflection", 174 | "invocations": [{ 175 | "owner": "java.lang.reflect.Method", 176 | "method": { 177 | "name": "invoke" 178 | } 179 | }] 180 | }] 181 | } 182 | ``` 183 | 184 | #### Find String instantiations 185 | 186 | It is the same than any method invocation, but the name of the method in this case, should be ``````. 187 | 188 | ```json 189 | { 190 | "rules": [{ 191 | "name" : "String instantiation", 192 | "invocations" : [{ 193 | "owner" : "java.lang.String", 194 | "method" : { 195 | "name" : "" 196 | } 197 | }] 198 | }] 199 | } 200 | ``` 201 | This rule will find occurrences of: 202 | ```java 203 | [...] 204 | String s = new String("foo"); 205 | [...] 206 | ``` 207 | 208 | #### Deserialization usage 209 | In this example, we want to find deserialization usages (not classes defining serialization behaviors like in the previous examples). Deserialization happens when ```ObjectInputStream.readObject()``` is invoked. for example in this code snippet: 210 | 211 | ```java 212 | ObjectInputStream in = new ObjectInputStream(fileInputStream); 213 | Object o = in.readObject(); 214 | ``` 215 | 216 | So we need to find method invocations from ```ObjectInputStream``` named ```readObject```. But it will find a lot of false positives in a researching context, because when a class defines custom deserialization, they make an invocation to this method inside a ```private void readObject(ObjectInputStream in)``` method, and that would pollute the report too much. If we want to exclude those cases and keep only genuine deserialization, ```notFrom``` property can be used: 217 | 218 | ```json 219 | { 220 | "rules": [{ 221 | "name": "Deserialization usage", 222 | "invocations": [{ 223 | "owner": "java.io.ObjectInputStream", 224 | "method": { 225 | "name": "readObject" 226 | }, 227 | "notFrom": { 228 | "name": "readObject", 229 | "visibility": "private" 230 | } 231 | }] 232 | }] 233 | } 234 | ``` 235 | This file will find ```java.io.ObjectInputStream.readObject()``` invocations if the invocation is not done inside ```private void readObject(ObjectInputStream in)``` method. 236 | 237 | A class compiled with this code will not be reported: 238 | 239 | ```java 240 | private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 241 | Object o = in.readObject(); 242 | } 243 | ``` 244 | But this one will be reported: 245 | 246 | ```java 247 | public Object deserializeObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 248 | Object o = in.readObject(); 249 | return o; 250 | } 251 | ``` 252 | 253 | The property ```from``` can be set in invocations in exactly the same way than ```notFrom```, but the result will be the opposite: it will only match if the invocation is made from the defined method. 254 | 255 | #### Java servlets 256 | 257 | The property ```superClass``` can be used in this case. If we want to find all classes extending ```javax.servlet.http.HttpServlet```, a rule can be: 258 | 259 | ```json 260 | { 261 | "rules": [{ 262 | "name": "Java servlets", 263 | "superClass" : "javax.servlet.http.HttpServlet" 264 | }] 265 | } 266 | 267 | ``` 268 | 269 | #### Interface implementations 270 | 271 | A rule can be written to find classes implementing an array of interfaces. if more than one interface is defined in the rule, the class has to implement all of them to be reported. If we want to find classes implementing ```javax.net.ssl.X509TrustManager```, the rule would be: 272 | 273 | ```json 274 | { 275 | "rules": [{ 276 | "name": "X509TrustManager implementations", 277 | "interfaces" : ["javax.net.ssl.X509TrustManager"] 278 | }] 279 | } 280 | ``` 281 | 282 | Please note that ```interfaces``` is an *array*, so make sure you add the strings between square brackets, e.g: ```["interface1", "interface2", ...]```. 283 | 284 | #### Find Spring endpoints 285 | 286 | Annotations are also supported. Multiple annotations properties can be defined in a rule (finding class annotations), in methods o variables (parameters or local variables). If all of them are found in the analyzed class, it will be reported. 287 | For example, if we want to find Spring endpoints, we would search for classes or methods annotated with ```org.springframework.web.bind.annotation.RequestMapping```. So, the rule can be: 288 | 289 | ```json 290 | { 291 | "rules": [{ 292 | "name": "Spring endpoint - class annotation", 293 | "annotations" : [{ 294 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 295 | }] 296 | }, 297 | { 298 | "name": "Spring endpoint - method annotation", 299 | "methods" : [{ 300 | "annotations" : [{ 301 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 302 | }] 303 | }] 304 | }] 305 | } 306 | ``` 307 | 308 | #### Find fields 309 | 310 | The property ```rule.fields``` can be used to find class fields. If we want to find private String fields with password names, a rule like this one could be used: 311 | 312 | ```json 313 | { 314 | "rules": [{ 315 | "name" : "Password fields", 316 | "fields" : [ 317 | { 318 | "visibility" : "private", 319 | "type" : "java.lang.String", 320 | "nameRegex" : "(password|pass|psswd|passwd)" 321 | } 322 | ] 323 | }] 324 | } 325 | ``` 326 | 327 | #### Find variables 328 | 329 | To find variables, ```rule.variables``` can be used. This property will report local variables and method arguments variables. 330 | If we want to find all variables of type ```javax.servlet.http.Part```, a rule could be: 331 | 332 | ```json 333 | { 334 | "rules": [{ 335 | "name" : "Servlet upload file", 336 | "methods" : [{ 337 | "variables" : [{ 338 | "type" : "javax.servlet.http.Part" 339 | }] 340 | }] 341 | }] 342 | } 343 | ``` 344 | 345 | #### Define multiple rules 346 | Multiple rules can be defined in the same JSON file. They will be processed and reported separately and they will not affect each other. We can combine some of the previous examples rules: 347 | 348 | ```json 349 | { 350 | "rules": [{ 351 | "name": "Custom deserialization", 352 | "methods": [{ 353 | "name": "readObject", 354 | "visibility": "private", 355 | "parameters": [{ 356 | "type" : "java.io.ObjectInputStream" 357 | }] 358 | }] 359 | },{ 360 | "name": "Method invocation by reflection", 361 | "invocations": [{ 362 | "owner": "java.lang.reflect.Method", 363 | "method": { 364 | "name": "invoke" 365 | } 366 | }] 367 | }] 368 | } 369 | ``` 370 | 371 | Here, we have two rules ("Custom deserialization" and "Method invocation by reflection"). They will be processed as if you do it in two separated executions. And a report per rule will be generated. If the rules have the same name, they will be reported in the same file. 372 | 373 | ## Custom Java rules 374 | 375 | The project can be downloaded and built to add more complex custom rules in Java code that are not covered by the JSON format. There are already three examples under the package ```net.nandgr.cba.visitor.checks```. Those are ```CustomDeserializationCheck, DeserializationCheck and InvokeMethodCheck```. You can create your own rules by extending ```net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor```. 376 | 377 | ## Reports 378 | 379 | As mentioned above, the reports are created by default under ```report``` folder. Every rule will have a separate file unless they have the same name. 380 | If the report is too big, you can split it using the ```-i,--items-report ``` parameter, each of them will hold the argument specified or less (if it is the last one). 381 | Every reported item, specifies the jar where it is found, the class name and the method name (if it is relevant). It also shows the decompiled version of the class to ease a quick visual check. 382 | Example of how the items are shown for a rule to find ```java.io.File``` instantiations: 383 | 384 | ![Report example](images/report_image.png) 385 | 386 | ### Call graph 387 | 388 | When searching for security bugs it is very useful to have a call graph. At the moment, a simple [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) compatible file is created under the ```report``` directory. 389 | The graph contains all the possible flows where the found issues can be invoked from. For example, if a rule to find deserialization is used, 390 | a graph containing all possible paths leading to the method that calls the deserialization will be generated. 391 | 392 | The file is ```call-graph.dot``` and it would look like this (this is an extremely simple example): 393 | 394 | ``` 395 | graph callGraph { 396 | "demo.callgraph.Class1:method1" -- "demo.callgraph.Class2:method2" 397 | "demo.callgraph.Class3:method3" -- "demo.callgraph.Class2:method2" 398 | } 399 | ``` 400 | 401 | To display it in a visual way, ```DOT``` can be used (or any compatible software). For example, to convert the file to ```svg```: 402 | 403 | ``` 404 | dot -Tsvg call-graph.dot -o call-graph.svg 405 | ``` 406 | 407 | This is done automatically by default if DOT is found in the system PATH. If not, ```DOT``` can be installed in Debian based systems using ```sudo apt-get install graphviz```. 408 | 409 | It will create a SVG file named ```call-graph.svg``` that can be converted into PNG or visualized using programs like ```inkscape``` or just ```firefox```. 410 | 411 | A very simple example of the above file call-graph.dot, would be: 412 | 413 | ![Graph example](images/graph.png) 414 | 415 | There are some limitations, like for example, if the searched item is in a ```java.lang.Runnable.run()``` or similar method, it will not find where the thread is executed from. 416 | Also, the graph is cleaning cycles to avoid ```StackOverflowError```s, it is made in a bit of conservative way so the memory of the system is not drained during an analysis of a large directory. 417 | 418 | More options will be added in future versions. 419 | 420 | ## Command line examples 421 | 422 | #### Run an analysis using a JSON file 423 | ``` 424 | java -jar cba-cli-.jar -a /path/with/jars -f /path/with/json/file/rules.json 425 | ``` 426 | #### Run an analysis using a Java custom rule 427 | To use custom java rules, class names have to be specified as arguments of ```-c```. 428 | ``` 429 | java -jar cba-cli-.jar -a /path/with/jars -c DeserializationCheck 430 | ``` 431 | Accepts a space separated list, so multiple custom rules can be defined (each of the rules will create a separate report): 432 | ``` 433 | java -jar cba-cli-.jar -a /path/with/jars -c DeserializationCheck InvokeMethodCheck CustomDeserializationCheck YourCustomRule 434 | ``` 435 | #### Combine JSON and custom Java rules 436 | ``` 437 | java -jar cba-cli-.jar -a /path/with/jars -f /path/with/json/file/rules.json -c YourCustomRule1 YourCustomRule2 438 | ``` 439 | 440 | #### Increase verbosity 441 | 442 | To find errors, verbosity can be increased. 443 | Debug level: 444 | ``` 445 | java -jar cba-cli-.jar -a /path/with/jars -c YourCustomRule1 -v 446 | ``` 447 | Trace level: 448 | ``` 449 | java -jar cba-cli-.jar -a /path/with/jars -c YourCustomRule1 -vv 450 | ``` 451 | 452 | ## Analyze Android APKs 453 | 454 | At the moment, the APK has to be converted to JAR first to be analyzed. 455 | 456 | * Download dex2jar : https://github.com/pxb1988/dex2jar 457 | * Convert DEX to JAR 458 | * ```d2j-dex2jar.sh -f -o app_to_analyze.jar app_to_analyze.apk``` 459 | * Run cba-cli.jar as usual passing as ```-a``` parameter the directory containing the converted jar file. 460 | 461 | ## Build and run the project 462 | 463 | There is already an executable jar file under ```bin``` directory at: [https://github.com/fergarrui/custom-bytecode-analyzer/blob/master/bin/cba-cli-0.1-SNAPSHOT.jar](https://github.com/fergarrui/custom-bytecode-analyzer/blob/master/bin/cba-cli-0.1-SNAPSHOT.jar) . If you want to do modifications or add custom rules, the project can be built doing: 464 | 465 | ``` 466 | git clone https://github.com/fergarrui/custom-bytecode-analyzer.git 467 | cd custom-bytecode-analyzer 468 | mvn clean package 469 | ``` 470 | Two jars will be generated under ```target``` folder. ```cba-cli-.jar``` contains all dependencies and is executable. Can be run using ```java -jar cba-cli-.jar``` 471 | -------------------------------------------------------------------------------- /examples/custom-message-digest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Custom Message Digest", 4 | "superClass" : "java.security.MessageDigest" 5 | }] 6 | } 7 | -------------------------------------------------------------------------------- /examples/deserialization-usage.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Deserialization usage", 4 | "invocations": [{ 5 | "owner": "java.io.ObjectInputStream", 6 | "method": { 7 | "name": "readObject" 8 | }, 9 | "notFrom": { 10 | "name": "readObject", 11 | "visibility": "private" 12 | } 13 | }] 14 | }] 15 | } 16 | -------------------------------------------------------------------------------- /examples/file-creation.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "File creation - java.io.File", 4 | "invocations": [{ 5 | "owner": "java.io.File", 6 | "method": { 7 | "name": "" 8 | } 9 | }] 10 | }, 11 | { 12 | "name": "File creation - java.io.FileInputStream", 13 | "invocations": [{ 14 | "owner": "java.io.File", 15 | "method": { 16 | "name": "" 17 | } 18 | }] 19 | }, 20 | { 21 | "name": "File creation - java.io.FileReader", 22 | "invocations": [{ 23 | "owner": "java.io.FileReader", 24 | "method": { 25 | "name": "" 26 | } 27 | }] 28 | }, 29 | { 30 | "name": "File creation - java.io.RandomAccessFile", 31 | "invocations": [{ 32 | "owner": "java.io.RandomAccessFile", 33 | "method": { 34 | "name": "" 35 | } 36 | }] 37 | }, 38 | { 39 | "name": "File creation - java.io.FileWriter", 40 | "invocations": [{ 41 | "owner": "java.io.FileWriter", 42 | "method": { 43 | "name": "" 44 | } 45 | }] 46 | }, 47 | { 48 | "name": "File creation - java.io.FileOutputStream", 49 | "invocations": [{ 50 | "owner": "java.io.FileOutputStream", 51 | "method": { 52 | "name": "" 53 | } 54 | }] 55 | }] 56 | } 57 | -------------------------------------------------------------------------------- /examples/file-upload.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "File upload Java servlet", 4 | "methods" : [{ 5 | "variables" : [{ 6 | "type" : "javax.servlet.http.Part" 7 | }] 8 | }] 9 | }, 10 | { 11 | "name": "File upload Apache Commons", 12 | "methods" : [{ 13 | "variables" : [{ 14 | "type" : "org.apache.commons.fileupload.FileItem" 15 | }] 16 | }] 17 | }, 18 | { 19 | "name": "File upload Spring MultiPartFile", 20 | "methods" : [{ 21 | "variables" : [{ 22 | "type": "org.springframework.web.multipart.MultipartFile" 23 | }] 24 | }] 25 | }, 26 | { 27 | "name": "File upload Jersey", 28 | "methods" : [{ 29 | "parameters" : [{ 30 | "annotations" : [{ 31 | "type" : "com.sun.jersey.multipart.FormDataParam" 32 | }] 33 | }] 34 | }] 35 | }] 36 | } 37 | -------------------------------------------------------------------------------- /examples/http-request-headers.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Request Headers use", 4 | "invocations": [{ 5 | "owner": "javax.servlet.http.HttpServletRequest", 6 | "method": { 7 | "name": "getHeader" 8 | } 9 | }] 10 | }, 11 | { 12 | "name": "Request Headers use (cookies)", 13 | "invocations": [{ 14 | "owner": "javax.servlet.http.HttpServletRequest", 15 | "method": { 16 | "name": "getCookies" 17 | } 18 | }] 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /examples/http-servlet.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Java servlets", 4 | "superClass" : "javax.servlet.http.HttpServlet" 5 | }] 6 | } 7 | -------------------------------------------------------------------------------- /examples/jax-rs-endpoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "JAX-RS Endpoint", 4 | "annotations" : [{ 5 | "type" : "javax.ws.rs.Path" 6 | }] 7 | }, 8 | { 9 | "name": "JAX-RS Endpoint", 10 | "methods" : [{ 11 | "annotations" : [{ 12 | "type" : "javax.ws.rs.Path" 13 | }] 14 | }] 15 | }] 16 | } 17 | -------------------------------------------------------------------------------- /examples/jax-ws-endpoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "JAX-WS endpoint", 4 | "methods" : [{ 5 | "annotations" : [{ 6 | "type" : "javax.jws.WebMethod" 7 | }] 8 | }] 9 | }] 10 | } 11 | -------------------------------------------------------------------------------- /examples/response-redirect.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Response Redirect", 4 | "invocations": [{ 5 | "owner": "javax.servlet.http.HttpServletResponse", 6 | "method": { 7 | "name": "sendRedirect" 8 | } 9 | }] 10 | }] 11 | } 12 | -------------------------------------------------------------------------------- /examples/rules-all.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Custom Message Digest", 4 | "superClass" : "java.security.MessageDigest" 5 | }, 6 | { 7 | "name": "Deserialization usage", 8 | "invocations": [{ 9 | "owner": "java.io.ObjectInputStream", 10 | "method": { 11 | "name": "readObject" 12 | }, 13 | "notFrom": { 14 | "name": "readObject", 15 | "visibility": "private" 16 | } 17 | }] 18 | }, 19 | { 20 | "name": "File creation - java.io.File", 21 | "invocations": [{ 22 | "owner": "java.io.File", 23 | "method": { 24 | "name": "" 25 | } 26 | }] 27 | }, 28 | { 29 | "name": "File creation - java.io.FileInputStream", 30 | "invocations": [{ 31 | "owner": "java.io.File", 32 | "method": { 33 | "name": "" 34 | } 35 | }] 36 | }, 37 | { 38 | "name": "File creation - java.io.FileReader", 39 | "invocations": [{ 40 | "owner": "java.io.FileReader", 41 | "method": { 42 | "name": "" 43 | } 44 | }] 45 | }, 46 | { 47 | "name": "File creation - java.io.RandomAccessFile", 48 | "invocations": [{ 49 | "owner": "java.io.RandomAccessFile", 50 | "method": { 51 | "name": "" 52 | } 53 | }] 54 | }, 55 | { 56 | "name": "File creation - java.io.FileWriter", 57 | "invocations": [{ 58 | "owner": "java.io.FileWriter", 59 | "method": { 60 | "name": "" 61 | } 62 | }] 63 | }, 64 | { 65 | "name": "File creation - java.io.FileOutputStream", 66 | "invocations": [{ 67 | "owner": "java.io.FileOutputStream", 68 | "method": { 69 | "name": "" 70 | } 71 | }] 72 | }, 73 | { 74 | "name": "File upload Java servlet", 75 | "methods" : [{ 76 | "variables" : [{ 77 | "type" : "javax.servlet.http.Part" 78 | }] 79 | }] 80 | }, 81 | { 82 | "name": "File upload Apache Commons", 83 | "methods" : [{ 84 | "variables" : [{ 85 | "type" : "org.apache.commons.fileupload.FileItem" 86 | }] 87 | }] 88 | }, 89 | { 90 | "name": "File upload Spring MultiPartFile", 91 | "methods" : [{ 92 | "variables" : [{ 93 | "type": "org.springframework.web.multipart.MultipartFile" 94 | }] 95 | }] 96 | }, 97 | { 98 | "name": "File upload Jersey", 99 | "methods" : [{ 100 | "parameters" : [{ 101 | "annotations" : [{ 102 | "type" : "com.sun.jersey.multipart.FormDataParam" 103 | }] 104 | }] 105 | }] 106 | }, 107 | { 108 | "name": "Request Headers use", 109 | "invocations": [{ 110 | "owner": "javax.servlet.http.HttpServletRequest", 111 | "method": { 112 | "name": "getHeader" 113 | } 114 | }] 115 | }, 116 | { 117 | "name": "Request Headers use (cookies)", 118 | "invocations": [{ 119 | "owner": "javax.servlet.http.HttpServletRequest", 120 | "method": { 121 | "name": "getCookies" 122 | } 123 | }] 124 | }, 125 | { 126 | "name": "Java servlets", 127 | "superClass" : "javax.servlet.http.HttpServlet" 128 | }, 129 | { 130 | "name": "JAX-RS Endpoint", 131 | "annotations" : [{ 132 | "type" : "javax.ws.rs.Path" 133 | }] 134 | }, 135 | { 136 | "name": "JAX-RS Endpoint", 137 | "methods" : [{ 138 | "annotations" : [{ 139 | "type" : "javax.ws.rs.Path" 140 | }] 141 | }] 142 | }, 143 | { 144 | "name": "JAX-WS endpoint", 145 | "methods" : [{ 146 | "annotations" : [{ 147 | "type" : "javax.jws.WebMethod" 148 | }] 149 | }] 150 | }, 151 | { 152 | "name": "Response Redirect", 153 | "invocations": [{ 154 | "owner": "javax.servlet.http.HttpServletResponse", 155 | "method": { 156 | "name": "sendRedirect" 157 | } 158 | }] 159 | }, 160 | { 161 | "name": "Spring endpoint - class annotation", 162 | "annotations" : [{ 163 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 164 | }] 165 | }, 166 | { 167 | "name": "Spring endpoint - method annotation", 168 | "methods" : [{ 169 | "annotations" : [{ 170 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 171 | }] 172 | }] 173 | }, 174 | { 175 | "name": "Struts1 endpoint", 176 | "superClass" : "org.apache.struts.action.Action" 177 | }, 178 | { 179 | "name": "X509TrustManager implementation", 180 | "interfaces" : ["javax.net.ssl.X509TrustManager"] 181 | } 182 | ] 183 | } 184 | -------------------------------------------------------------------------------- /examples/spring-mvc-endpoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Spring endpoint - class annotation", 4 | "annotations" : [{ 5 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 6 | }] 7 | }, 8 | { 9 | "name": "Spring endpoint - method annotation", 10 | "methods" : [{ 11 | "annotations" : [{ 12 | "type" : "org.springframework.web.bind.annotation.RequestMapping" 13 | }] 14 | }] 15 | }] 16 | } -------------------------------------------------------------------------------- /examples/struts1-endpoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "Struts1 endpoint", 4 | "superClass" : "org.apache.struts.action.Action" 5 | }] 6 | } 7 | -------------------------------------------------------------------------------- /examples/x509trust-manager-impl.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "X509TrustManager implementation", 4 | "interfaces" : ["javax.net.ssl.X509TrustManager"] 5 | }] 6 | } -------------------------------------------------------------------------------- /images/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fergarrui/custom-bytecode-analyzer/e907055ced6e6e2dfbd040166155f57245b31a76/images/graph.png -------------------------------------------------------------------------------- /images/report_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fergarrui/custom-bytecode-analyzer/e907055ced6e6e2dfbd040166155f57245b31a76/images/report_image.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.nandgr.cba 8 | custom-bytecode-analyzer 9 | 0.1.1-SNAPSHOT 10 | jar 11 | 12 | 13 | 5.1 14 | 1.0 15 | 1.3.2 16 | 1.3.1 17 | 2.8.0 18 | 1.7 19 | 1.8 20 | 2.4.1 21 | 1.7.22 22 | 1.1.8 23 | 2.6 24 | 4.12 25 | cba-cli-${version} 26 | 0.5.32 27 | 1.0.1 28 | 29 | 30 | 31 | 32 | org.ow2.asm 33 | asm 34 | ${asm.version} 35 | 36 | 37 | org.ow2.asm 38 | asm-debug-all 39 | ${asm.version} 40 | 41 | 42 | com.google.collections 43 | google-collections 44 | ${google.collections.version} 45 | 46 | 47 | org.apache.commons 48 | commons-io 49 | ${apache.commons.io.version} 50 | 51 | 52 | commons-cli 53 | commons-cli 54 | ${apache.commons.cli.version} 55 | 56 | 57 | com.google.code.gson 58 | gson 59 | ${gson.version} 60 | 61 | 62 | org.slf4j 63 | slf4j-api 64 | ${slf4j.api.version} 65 | 66 | 67 | ch.qos.logback 68 | logback-classic 69 | ${logback.version} 70 | 71 | 72 | commons-lang 73 | commons-lang 74 | ${commons.lang.version} 75 | 76 | 77 | 78 | org.apache.velocity 79 | velocity 80 | ${velocity.version} 81 | 82 | 83 | 84 | org.jgrapht 85 | jgrapht-core 86 | ${jgrapht.version} 87 | 88 | 89 | 90 | 91 | org.bitbucket.mstrobel 92 | procyon-compilertools 93 | ${procyon-version} 94 | 95 | 96 | 97 | junit 98 | junit 99 | ${junit.version} 100 | test 101 | 102 | 103 | 104 | 105 | 106 | maven-compiler-plugin 107 | 108 | ${java.version} 109 | ${java.version} 110 | 111 | 112 | 113 | org.apache.maven.plugins 114 | maven-assembly-plugin 115 | ${mvn.assembly.plugin.version} 116 | 117 | 118 | jar-with-dependencies 119 | 120 | ${jar.cli} 121 | false 122 | 123 | 124 | net.nandgr.cba.App 125 | 126 | 127 | 128 | 129 | 130 | make-assembly 131 | package 132 | 133 | single 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba; 18 | 19 | import net.nandgr.cba.cli.CliArguments; 20 | import net.nandgr.cba.cli.CliHelper; 21 | import net.nandgr.cba.exception.BadArgumentsException; 22 | import java.io.IOException; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | public class App { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(App.class); 29 | 30 | private App() { 31 | throw new IllegalAccessError("Cannot instantiate this main class."); 32 | } 33 | 34 | public static void main(String[] args) { 35 | 36 | try { 37 | CliArguments.parseArguments(args); 38 | } catch (BadArgumentsException e) { 39 | CliArguments.badArgument(e); 40 | } 41 | 42 | long startTime = System.nanoTime(); 43 | logger.info("Starting Analyzer..."); 44 | try { 45 | ArchiveWalker archiveWalker = new ArchiveWalker(CliHelper.getPathToAnalyze(), CliHelper.getMaxThreads()); 46 | archiveWalker.walk(); 47 | long endTime = System.nanoTime(); 48 | logger.info("Analysis done in {} seconds.", (endTime - startTime) / 1000000000.0); 49 | } catch (ReflectiveOperationException e) { 50 | CliArguments.badArgument(e); 51 | } catch (IOException e) { 52 | logger.error("Error while analyzing provided path.",e); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/ArchiveAnalyzerCallable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba; 18 | 19 | import net.nandgr.cba.callgraph.runnable.ClassCallGraph; 20 | import net.nandgr.cba.decompile.Decompiler; 21 | import net.nandgr.cba.decompile.ZipEntryDecompiler; 22 | import net.nandgr.cba.report.ReportItem; 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.nio.file.Path; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.concurrent.Callable; 29 | import java.util.zip.ZipEntry; 30 | import java.util.zip.ZipFile; 31 | import org.objectweb.asm.ClassReader; 32 | import org.objectweb.asm.tree.ClassNode; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | public class ArchiveAnalyzerCallable implements Callable { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(ArchiveAnalyzerCallable.class); 39 | 40 | private static final String CLASS_EXTENSION = ".class"; 41 | private final Path jarPath; 42 | private final ByteCodeAnalyzer byteCodeAnalyzer; 43 | 44 | public ArchiveAnalyzerCallable(Path jarPath, ByteCodeAnalyzer byteCodeAnalyzer) { 45 | this.jarPath = jarPath; 46 | this.byteCodeAnalyzer = byteCodeAnalyzer; 47 | } 48 | 49 | @Override 50 | public List call() throws Exception { 51 | File jarFile = new File(jarPath.toUri()); 52 | List reportItems = new ArrayList<>(); 53 | ZipFile zipFile = new ZipFile(jarFile); 54 | zipFile.stream().filter(ArchiveAnalyzerCallable::isClassFile) 55 | .forEach(zipEntry -> { 56 | try { 57 | String zipEntryName = zipEntry.getName(); 58 | logger.debug("Class found: {}", zipEntryName); 59 | 60 | // retrieving a new inputStream - do not extract variable 61 | ClassReader classReader = new ClassReader(zipFile.getInputStream(zipEntry)); 62 | ClassNode classNode = new ClassNode(); 63 | classReader.accept(classNode,0); 64 | List analyzeReportItems = byteCodeAnalyzer.analyze(classNode); 65 | 66 | ClassCallGraph classCallGraph = new ClassCallGraph(classNode); 67 | classCallGraph.populateClassGraph(); 68 | 69 | String decompiledFile = null; 70 | if (!analyzeReportItems.isEmpty()) { 71 | Decompiler decompiler = new ZipEntryDecompiler(); 72 | // retrieving a new inputStream - do not extract variable 73 | decompiledFile = decompiler.decompile(zipFile.getInputStream(zipEntry), zipEntryName); 74 | } 75 | 76 | addContextToReportItems(analyzeReportItems, jarPath.toAbsolutePath().toString(), zipEntryName, decompiledFile); 77 | reportItems.addAll(analyzeReportItems); 78 | } catch (IOException e) { 79 | logger.error("Error while analyzing Jar internals.", e); 80 | } 81 | }); 82 | zipFile.close(); 83 | 84 | return reportItems; 85 | } 86 | 87 | public static void addContextToReportItems(List reportItems, String jarPath, String className, String decompiledFile) { 88 | reportItems.stream().forEach(reportItem -> { 89 | reportItem.setJarPath(jarPath); 90 | reportItem.setClassName(className); 91 | reportItem.setDecompiledFile(decompiledFile); 92 | }); 93 | } 94 | 95 | private static boolean isClassFile(ZipEntry zipEntry) { 96 | return zipEntry.getName().endsWith(CLASS_EXTENSION); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/ArchiveWalker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba; 18 | 19 | import net.nandgr.cba.callgraph.model.GraphHolder; 20 | import net.nandgr.cba.cli.CliHelper; 21 | import net.nandgr.cba.report.ReportBuilder; 22 | import net.nandgr.cba.report.ReportItem; 23 | import java.io.IOException; 24 | import java.nio.file.Files; 25 | import java.nio.file.Paths; 26 | import java.util.ArrayList; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.concurrent.ExecutionException; 31 | import java.util.concurrent.ExecutorService; 32 | import java.util.concurrent.Executors; 33 | import java.util.concurrent.Future; 34 | import java.util.concurrent.TimeUnit; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | public class ArchiveWalker { 39 | 40 | private static final Logger logger = LoggerFactory.getLogger(ArchiveWalker.class); 41 | private static final String JAR_EXTENSION = ".jar"; 42 | private static final String WAR_EXTENSION = ".war"; 43 | private final String path; 44 | private final ExecutorService executorService; 45 | private final ByteCodeAnalyzer byteCodeAnalyzer; 46 | 47 | public ArchiveWalker(String path, int maxThreads) throws ReflectiveOperationException, IOException { 48 | this.path = path; 49 | this.executorService = Executors.newFixedThreadPool(maxThreads); 50 | this.byteCodeAnalyzer = new CustomByteCodeAnalyzer(CliHelper.hasCustomFile(), CliHelper.hasChecks(), CliHelper.getRules()); 51 | } 52 | 53 | public void walk() throws IOException { 54 | logger.info("Walking through JAR files in: " + path); 55 | List>> reportItemsFutureList = new ArrayList<>(); 56 | Files.walk(Paths.get(path)) 57 | .filter(filePath -> { 58 | String fileString = filePath.toUri().toString(); 59 | return fileString.endsWith(JAR_EXTENSION) || fileString.endsWith(WAR_EXTENSION); 60 | }) 61 | .forEach(jarPath -> { 62 | logger.info("Analyzing: " + jarPath.toUri().toString()); 63 | ArchiveAnalyzerCallable archiveAnalyzerCallable = new ArchiveAnalyzerCallable(jarPath, byteCodeAnalyzer); 64 | Future> reportItemsFuture = executorService.submit(archiveAnalyzerCallable); 65 | reportItemsFutureList.add(reportItemsFuture); 66 | }); 67 | logger.info("Shutting down walker..."); 68 | executorService.shutdown(); 69 | try { 70 | logger.info("Waiting for analysis... This may take a while"); 71 | executorService.awaitTermination(1, TimeUnit.DAYS); 72 | } catch (InterruptedException e) { 73 | logger.error("Executor service interrupted. This shouldn't happen.", e); 74 | Thread.currentThread().interrupt(); 75 | } 76 | Map> ruleNameReportItemsGrouped = groupReportItems(reportItemsFutureList); 77 | executorService.shutdownNow(); 78 | logger.info("Walker shutdown."); 79 | logger.info("Building report..."); 80 | ReportBuilder.saveAsHtml(ruleNameReportItemsGrouped); 81 | GraphHolder.createGraph(); 82 | GraphHolder.filterGraph(ruleNameReportItemsGrouped); 83 | GraphHolder.saveCallGraph(); 84 | } 85 | 86 | private Map> groupReportItems(List>> reportItemsFutureList) { 87 | Map> groupedItems = new HashMap<>(); 88 | reportItemsFutureList.stream().forEach(futureList -> { 89 | try { 90 | List reportItems = futureList.get(); 91 | reportItems.stream().forEach(reportItem -> { 92 | String ruleName = reportItem.getRuleName(); 93 | if(groupedItems.containsKey(ruleName)) { 94 | groupedItems.get(ruleName).add(reportItem); 95 | } else { 96 | List reportItemsValue = new ArrayList<>(); 97 | reportItemsValue.add(reportItem); 98 | groupedItems.put(ruleName, reportItemsValue); 99 | } 100 | }); 101 | } catch (ExecutionException | InterruptedException e) { 102 | logger.error("Error while building the report. Still analyzers alive?", e); 103 | } 104 | }); 105 | return groupedItems; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/ByteCodeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba; 18 | 19 | import net.nandgr.cba.report.ReportItem; 20 | import java.util.List; 21 | import org.objectweb.asm.tree.ClassNode; 22 | 23 | @FunctionalInterface 24 | public interface ByteCodeAnalyzer { 25 | List analyze(ClassNode classNode); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/CustomByteCodeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba; 18 | 19 | import java.lang.reflect.InvocationTargetException; 20 | import net.nandgr.cba.custom.model.Annotation; 21 | import net.nandgr.cba.custom.model.Field; 22 | import net.nandgr.cba.custom.model.Rule; 23 | import net.nandgr.cba.custom.model.Rules; 24 | import net.nandgr.cba.custom.visitor.CustomClassAnnotationVisitor; 25 | import net.nandgr.cba.custom.visitor.CustomClassInterfacesVisitor; 26 | import net.nandgr.cba.custom.visitor.CustomClassSuperClassVisitor; 27 | import net.nandgr.cba.custom.visitor.CustomFieldVisitor; 28 | import net.nandgr.cba.custom.visitor.CustomMethodInvocationVisitor; 29 | import net.nandgr.cba.custom.visitor.base.CustomVisitor; 30 | import net.nandgr.cba.report.ReportItem; 31 | import net.nandgr.cba.cli.CliHelper; 32 | import net.nandgr.cba.custom.model.Invocation; 33 | import net.nandgr.cba.custom.model.Method; 34 | import net.nandgr.cba.custom.visitor.CustomMethodVisitor; 35 | import net.nandgr.cba.custom.visitor.RuleVisitorsAnalyzer; 36 | import java.io.ByteArrayInputStream; 37 | import java.io.IOException; 38 | import java.io.InputStream; 39 | import java.lang.reflect.Constructor; 40 | import java.util.ArrayList; 41 | import java.util.List; 42 | import org.apache.commons.io.IOUtils; 43 | import org.apache.commons.lang.StringUtils; 44 | import org.objectweb.asm.tree.ClassNode; 45 | import org.slf4j.Logger; 46 | import org.slf4j.LoggerFactory; 47 | 48 | public class CustomByteCodeAnalyzer implements ByteCodeAnalyzer { 49 | 50 | private static final Logger logger = LoggerFactory.getLogger(CustomByteCodeAnalyzer.class); 51 | 52 | private static final String CHECKS_PACKAGE = "net.nandgr.cba.visitor.checks."; 53 | private final List ruleVisitorsAnalyzers = new ArrayList<>(); 54 | 55 | public CustomByteCodeAnalyzer(boolean hasCustomFile, boolean hasChecks, Rules rules) throws ReflectiveOperationException, IOException { 56 | if (hasCustomFile) { 57 | populateCustomVisitors(rules); 58 | } 59 | if (hasChecks) { 60 | populateStaticVisitors(); 61 | } 62 | } 63 | 64 | private void populateCustomVisitors(Rules rules) throws IOException { 65 | logger.debug("Processing {} rule(s)", rules.getRules().size()); 66 | for (Rule rule : rules.getRules()) { 67 | logger.debug("Rule {} added.", rule.getName()); 68 | RuleVisitorsAnalyzer ruleVisitorsAnalyzer = new RuleVisitorsAnalyzer(); 69 | 70 | List methods = rule.getMethods(); 71 | addMethods(ruleVisitorsAnalyzer, methods, rule); 72 | 73 | List invocations = rule.getInvocations(); 74 | addInvocations(ruleVisitorsAnalyzer, invocations, rule); 75 | 76 | List interfaces = rule.getInterfaces(); 77 | addInterfaces(ruleVisitorsAnalyzer, interfaces, rule); 78 | 79 | String superClass = rule.getSuperClass(); 80 | addSuperClass(ruleVisitorsAnalyzer, superClass, rule); 81 | 82 | List annotations = rule.getAnnotations(); 83 | addAnnotations(ruleVisitorsAnalyzer, annotations, rule); 84 | 85 | List fields = rule.getFields(); 86 | addFields(ruleVisitorsAnalyzer, fields, rule); 87 | 88 | this.ruleVisitorsAnalyzers.add(ruleVisitorsAnalyzer); 89 | } 90 | logger.debug("Rules processed."); 91 | } 92 | 93 | private static void addFields(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, List fields, Rule rule) { 94 | if (fields != null && !fields.isEmpty()) { 95 | for (Field field : fields) { 96 | CustomFieldVisitor customFieldVisitor = new CustomFieldVisitor(field, rule.getName()); 97 | ruleVisitorsAnalyzer.getVisitorList().add(customFieldVisitor); 98 | } 99 | } 100 | } 101 | 102 | private void addAnnotations(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, List annotations, Rule rule) { 103 | if (annotations != null && !annotations.isEmpty()) { 104 | for (Annotation annotation : annotations) { 105 | CustomClassAnnotationVisitor customClassAnnotationVisitor = new CustomClassAnnotationVisitor(annotation, rule.getName()); 106 | ruleVisitorsAnalyzer.getVisitorList().add(customClassAnnotationVisitor); 107 | } 108 | } 109 | } 110 | 111 | private static void addSuperClass(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, String superClass, Rule rule) { 112 | if (!StringUtils.isBlank(superClass)) { 113 | CustomClassSuperClassVisitor customClassSuperClassVisitor = new CustomClassSuperClassVisitor(superClass, rule.getName()); 114 | ruleVisitorsAnalyzer.getVisitorList().add(customClassSuperClassVisitor); 115 | } 116 | } 117 | 118 | private static void addInterfaces(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, List interfaces, Rule rule) { 119 | if (interfaces != null && !interfaces.isEmpty()) { 120 | CustomClassInterfacesVisitor customClassInterfacesVisitor = new CustomClassInterfacesVisitor(interfaces, rule.getName()); 121 | ruleVisitorsAnalyzer.getVisitorList().add(customClassInterfacesVisitor); 122 | } 123 | } 124 | 125 | private static void addInvocations(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, List invocations, Rule rule) { 126 | if (invocations != null) { 127 | for (Invocation invocation : invocations) { 128 | CustomMethodInvocationVisitor customMethodInvocationVisitor = new CustomMethodInvocationVisitor(invocation, rule.getName()); 129 | ruleVisitorsAnalyzer.getVisitorList().add(customMethodInvocationVisitor); 130 | } 131 | } 132 | } 133 | 134 | private static void addMethods(RuleVisitorsAnalyzer ruleVisitorsAnalyzer, List methods, Rule rule) { 135 | if (methods != null) { 136 | for (Method method : methods) { 137 | CustomMethodVisitor customMethodVisitor = new CustomMethodVisitor(method, rule.getName()); 138 | ruleVisitorsAnalyzer.getVisitorList().add(customMethodVisitor); 139 | } 140 | } 141 | } 142 | 143 | private void populateStaticVisitors() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 144 | String[] checks = CliHelper.getChecks(); 145 | if (checks == null) { 146 | return; 147 | } 148 | List> classVisitorList = new ArrayList<>(); 149 | for (String check : checks) { 150 | Class checkClass = App.class.forName(CHECKS_PACKAGE + check); 151 | classVisitorList.add(checkClass); 152 | } 153 | for (Class classVisitor : classVisitorList) { 154 | Constructor constructor = classVisitor.getDeclaredConstructor(); 155 | CustomVisitor reporterClassVisitor = (CustomVisitor) constructor.newInstance(); 156 | RuleVisitorsAnalyzer ruleVisitorsAnalyzer = new RuleVisitorsAnalyzer(); 157 | ruleVisitorsAnalyzer.getVisitorList().add(reporterClassVisitor); 158 | this.ruleVisitorsAnalyzers.add(ruleVisitorsAnalyzer); 159 | } 160 | } 161 | 162 | @Override 163 | public List analyze(ClassNode classNode) { 164 | logger.debug("Analyzing file... "); 165 | final List reportItems = new ArrayList<>(); 166 | try { 167 | for (RuleVisitorsAnalyzer ruleVisitorsAnalyzer : ruleVisitorsAnalyzers) { 168 | reportItems.addAll(ruleVisitorsAnalyzer.runRules(classNode)); 169 | } 170 | } catch (Exception e) { 171 | logger.error("Error while analyzing inputStream", e); 172 | } 173 | logger.debug("File analyzed, {} issue(s) found.", reportItems.size()); 174 | return reportItems; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/graph/InvocationGraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.graph; 18 | 19 | import java.util.Collection; 20 | import net.nandgr.cba.callgraph.model.MethodGraph; 21 | 22 | /** 23 | * This interface contains and manages all relations between the method invocations. 24 | * @param Is the type of vertex (method) 25 | * @param Is the relation between two vertex. Should represent a method invocation. 26 | */ 27 | public interface InvocationGraph { 28 | 29 | /** 30 | * Standard contains method. 31 | * @param element element to find 32 | * @return a T element when found. Null of not found 33 | */ 34 | boolean contains(T element); 35 | 36 | /** 37 | * Adds an element to a parent. 38 | * @param element 39 | */ 40 | void add(T element, T parent); 41 | 42 | /** 43 | * @param element 44 | * @return A collection of C (invocations) from element to the beginning of the graph. 45 | */ 46 | Collection pathsToParents(T element); 47 | 48 | boolean findCycles(MethodGraph vertex); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/graph/InvocationGraphImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.graph; 18 | 19 | import java.util.Collection; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | import net.nandgr.cba.callgraph.model.Call; 23 | import net.nandgr.cba.callgraph.model.MethodGraph; 24 | import org.jgrapht.DirectedGraph; 25 | import org.jgrapht.alg.CycleDetector; 26 | import org.jgrapht.graph.DefaultDirectedGraph; 27 | import org.jgrapht.graph.DefaultEdge; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | public class InvocationGraphImpl implements InvocationGraph { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(InvocationGraphImpl.class); 34 | private final DirectedGraph directedGraph; 35 | 36 | public InvocationGraphImpl() { 37 | this.directedGraph = new DefaultDirectedGraph<>(DefaultEdge.class); 38 | } 39 | 40 | @Override 41 | public boolean contains(MethodGraph element) { 42 | return directedGraph.containsVertex(element); 43 | } 44 | 45 | @Override 46 | public void add(MethodGraph element, MethodGraph parent) { 47 | if (element.equals(parent)) { 48 | return; 49 | } 50 | directedGraph.addVertex(element); 51 | directedGraph.addVertex(parent); 52 | directedGraph.addEdge(parent, element); 53 | if (findCycles(element)) { 54 | directedGraph.removeEdge(parent, element); 55 | } 56 | } 57 | 58 | @Override 59 | public Collection pathsToParents(MethodGraph element) { 60 | Collection paths = new HashSet<>(); 61 | logger.debug("Finding path to parents for: {} ", element); 62 | if (!directedGraph.containsVertex(element)) { 63 | logger.warn("Graph does not contain vertex: {} . Returning empty collection.", element); 64 | return paths; 65 | } 66 | Set incomingEdges = directedGraph.incomingEdgesOf(element); 67 | if (incomingEdges.isEmpty()) { 68 | Call lonelyCall = new Call(element, null); 69 | paths.add(lonelyCall); 70 | return paths; 71 | } 72 | for (DefaultEdge incomingEdge : incomingEdges) { 73 | MethodGraph father = directedGraph.getEdgeSource(incomingEdge); 74 | logger.debug("Finding path to parents, element={}, father={} . About to find paths from father", element, father); 75 | Call call = new Call(father, element); 76 | paths.add(call); 77 | paths.addAll(pathsToParents(father)); 78 | } 79 | return paths; 80 | } 81 | 82 | @Override 83 | public boolean findCycles(MethodGraph vertex) { 84 | CycleDetector cycleDetector = new CycleDetector(directedGraph); 85 | return cycleDetector.detectCyclesContainingVertex(vertex); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/model/Call.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.model; 18 | 19 | public class Call { 20 | 21 | private final MethodGraph caller; 22 | private final MethodGraph called; 23 | 24 | public Call(MethodGraph caller, MethodGraph called) { 25 | this.caller = caller; 26 | this.called = called; 27 | } 28 | 29 | public MethodGraph getCaller() { 30 | return caller; 31 | } 32 | 33 | public MethodGraph getCalled() { 34 | return called; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object o) { 39 | if (this == o) return true; 40 | if (!(o instanceof Call)) return false; 41 | 42 | Call call = (Call) o; 43 | 44 | if (!getCaller().equals(call.getCaller())) return false; 45 | return getCalled() != null ? getCalled().equals(call.getCalled()) : call.getCalled() == null; 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | int result = getCaller().hashCode(); 51 | result = 31 * result + (getCalled() != null ? getCalled().hashCode() : 0); 52 | return result; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Call{" + 58 | "caller=" + caller + 59 | ", called=" + called + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/model/GraphHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.model; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.nio.file.Files; 22 | import java.nio.file.Paths; 23 | import java.util.Arrays; 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | import java.util.HashMap; 27 | import java.util.HashSet; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.regex.Pattern; 31 | import net.nandgr.cba.callgraph.graph.InvocationGraph; 32 | import net.nandgr.cba.callgraph.graph.InvocationGraphImpl; 33 | import net.nandgr.cba.cli.CliHelper; 34 | import net.nandgr.cba.custom.visitor.helper.StringsHelper; 35 | import net.nandgr.cba.report.ReportItem; 36 | import org.apache.commons.io.FileUtils; 37 | import org.objectweb.asm.Type; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | public class GraphHolder { 42 | 43 | private static final Logger logger = LoggerFactory.getLogger(GraphHolder.class); 44 | private static final Map> methodGraphMap = new HashMap<>(); 45 | private static final Collection graphCollection = Collections.synchronizedSet(new HashSet<>()); 46 | private static InvocationGraph invocationGraph = new InvocationGraphImpl(); 47 | 48 | public static void addCallGraph(Call call) { 49 | graphCollection.add(call); 50 | } 51 | 52 | public static void saveCallGraph() { 53 | for (Map.Entry> entry : methodGraphMap.entrySet()) { 54 | String ruleName = entry.getKey(); 55 | Collection methodGraphs = entry.getValue(); 56 | File callGraphFile = new File(getCallGraphFileName(ruleName)); 57 | try { 58 | File parentFile = callGraphFile.getParentFile(); 59 | if (!parentFile.exists()) { 60 | parentFile.mkdirs(); 61 | } 62 | callGraphFile.createNewFile(); 63 | String graphText = buildTextFromGraph(methodGraphs); 64 | FileUtils.writeStringToFile(callGraphFile, graphText); 65 | logger.info("Call graph written at: {}", callGraphFile.getAbsoluteFile()); 66 | dotToSVG(callGraphFile, callGraphFile.getParentFile().getAbsolutePath()); 67 | } catch (IOException e) { 68 | logger.error("Error when creating call graph file.", e); 69 | } 70 | } 71 | } 72 | 73 | private static void dotToSVG(File graphFile, String destFolder) { 74 | logger.info("Trying to convert to SVG... {}", graphFile.getName()); 75 | if (checkDotInClasspath()) { 76 | logger.info("Dot found in PATH."); 77 | if (!destFolder.endsWith(File.separator)) { 78 | destFolder += File.separator; 79 | } 80 | String svgFileName = destFolder + graphFile.getName().replace(".dot", ".svg"); 81 | String[] command = {"dot", "-Tsvg", graphFile.getAbsolutePath(), "-o", svgFileName}; 82 | try { 83 | Runtime.getRuntime().exec(command); 84 | } catch (IOException e) { 85 | logger.error("Error when trying to convert graph from DOT to SVG."); 86 | } 87 | } else { 88 | logger.warn("DOT not found. Please install it and make sure it is in the path. To convert the graph manually use: dot -Tsvg {} -o call-graph.svg", graphFile.getAbsolutePath()); 89 | } 90 | } 91 | 92 | private static boolean checkDotInClasspath() { 93 | final String DOT_EXEC_NAME = "dot"; 94 | return Arrays.stream(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))) 95 | .map(Paths::get) 96 | .anyMatch(path -> Files.exists(path.resolve(DOT_EXEC_NAME))); 97 | } 98 | 99 | private static String buildTextFromGraph(Collection calls) { 100 | StringBuilder stringBuilder = new StringBuilder("graph callGraph {" + System.lineSeparator()); 101 | 102 | for (Call call : calls) { 103 | MethodGraph caller = call.getCaller(); 104 | MethodGraph called = call.getCalled(); 105 | 106 | String callerOwner = caller.getOwner(); 107 | String callerName = caller.getName(); 108 | 109 | stringBuilder.append("\"").append(callerOwner).append(":").append(callerName).append("\""); 110 | if (called != null) { 111 | String calledOwner = called.getOwner(); 112 | String calledName = called.getName(); 113 | stringBuilder.append(" -- ") 114 | .append("\"").append(calledOwner).append(":").append(calledName).append("\""); 115 | } 116 | stringBuilder.append(System.lineSeparator()); 117 | } 118 | 119 | stringBuilder.append("}").append(System.lineSeparator()); 120 | return stringBuilder.toString(); 121 | } 122 | 123 | // Not used at the moment. Will be configurable to create a full graph of the analyzed directory. 124 | private static String buildTextFromGraph() { 125 | StringBuilder stringBuilder = new StringBuilder("graph callGraph {" + System.lineSeparator()); 126 | for (Call call : graphCollection) { 127 | MethodGraph caller = call.getCaller(); 128 | MethodGraph called = call.getCalled(); 129 | stringBuilder.append("\"").append(caller.getOwner()).append(":").append(caller.getName()).append("\"") 130 | .append(" -- ").append("\"").append(called.getOwner()).append(":").append(called.getName()).append("\"").append(System.lineSeparator()); 131 | } 132 | stringBuilder.append("}").append(System.lineSeparator()); 133 | return stringBuilder.toString(); 134 | } 135 | 136 | private static String getCallGraphFileName(String ruleName) { 137 | String callGraphFileName = CliHelper.getOutputDir(); 138 | if (!callGraphFileName.endsWith(File.separator)) { 139 | callGraphFileName += File.separator; 140 | } 141 | callGraphFileName += "call-graph-" + StringsHelper.spacesToDashesLowercase(ruleName) + ".dot"; 142 | return callGraphFileName; 143 | } 144 | 145 | public static void createGraph() { 146 | logger.info("Creating call graph... May take a while."); 147 | long start = System.currentTimeMillis(); 148 | for (Call call: graphCollection) { 149 | MethodGraph caller = call.getCaller(); 150 | MethodGraph called = call.getCalled(); 151 | invocationGraph.add(called, caller); 152 | } 153 | long end = System.currentTimeMillis(); 154 | logger.info("Call graph created in {} ms.", (end-start)); 155 | } 156 | 157 | public static void filterGraph(Map> reportItemsMap) { 158 | logger.info("Filtering graph..."); 159 | long start = System.currentTimeMillis(); 160 | if (invocationGraph == null) { 161 | return; 162 | } 163 | for (Map.Entry> entry : reportItemsMap.entrySet()) { 164 | String ruleName = entry.getKey(); 165 | List reportItems = entry.getValue(); 166 | for (ReportItem reportItem : reportItems) { 167 | // TODO refactor to static string constant 168 | String methodNameKey = "Method Name"; 169 | Map reportItemProperties = reportItem.getProperties(); 170 | if (reportItemProperties.containsKey(methodNameKey)) { 171 | 172 | String className = Type.getObjectType(reportItem.getClassName()).getClassName(); 173 | String methodName = reportItemProperties.get(methodNameKey); 174 | MethodGraph methodGraph = new MethodGraph(StringsHelper.removeClassSuffix(className), methodName); 175 | Collection calls = invocationGraph.pathsToParents(methodGraph); 176 | if (methodGraphMap.containsKey(ruleName)) { 177 | Collection alreadyExistingCalls = methodGraphMap.get(ruleName); 178 | alreadyExistingCalls.addAll(calls); 179 | } else { 180 | methodGraphMap.put(ruleName, calls); 181 | } 182 | } 183 | } 184 | } 185 | long end = System.currentTimeMillis(); 186 | logger.info("Graph filtered in {} ms.", (end-start)); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/model/MethodGraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.model; 18 | 19 | public class MethodGraph { 20 | 21 | private final String owner; 22 | private final String name; 23 | 24 | public MethodGraph(String owner, String name) { 25 | this.owner = owner; 26 | this.name = name; 27 | } 28 | 29 | public String getOwner() { 30 | return owner; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object o) { 39 | if (this == o) return true; 40 | if (!(o instanceof MethodGraph)) return false; 41 | 42 | MethodGraph that = (MethodGraph) o; 43 | 44 | if (!getOwner().equals(that.getOwner())) return false; 45 | return getName().equals(that.getName()); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | int result = getOwner().hashCode(); 51 | result = 31 * result + getName().hashCode(); 52 | return result; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "MethodGraph{" + 58 | "owner='" + owner + '\'' + 59 | ", name='" + name + '\'' + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/callgraph/runnable/ClassCallGraph.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.callgraph.runnable; 18 | 19 | import net.nandgr.cba.callgraph.model.Call; 20 | import net.nandgr.cba.callgraph.model.GraphHolder; 21 | import net.nandgr.cba.callgraph.model.MethodGraph; 22 | import org.objectweb.asm.Type; 23 | import org.objectweb.asm.tree.AbstractInsnNode; 24 | import org.objectweb.asm.tree.ClassNode; 25 | import org.objectweb.asm.tree.InsnList; 26 | import org.objectweb.asm.tree.MethodInsnNode; 27 | import org.objectweb.asm.tree.MethodNode; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | public class ClassCallGraph { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(ClassCallGraph.class); 34 | private final ClassNode classNode; 35 | 36 | public ClassCallGraph(ClassNode classNode) { 37 | this.classNode = classNode; 38 | } 39 | 40 | public void populateClassGraph() { 41 | String className = Type.getObjectType(classNode.name).getClassName(); 42 | logger.debug("Creating graph for class {}" , className); 43 | for (MethodNode methodNode : classNode.methods) { 44 | String methodName = methodNode.name; 45 | MethodGraph caller = new MethodGraph(className, methodName); 46 | InsnList instructions = methodNode.instructions; 47 | for (int i = 0; i < instructions.size(); i++) { 48 | AbstractInsnNode insnNode = instructions.get(i); 49 | if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) { 50 | MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; 51 | String calledOwner = Type.getObjectType(methodInsnNode.owner).getClassName(); 52 | String calledName = methodInsnNode.name; 53 | MethodGraph called = new MethodGraph(calledOwner, calledName); 54 | Call call = new Call(caller, called); 55 | if (!called.getOwner().equals("java.lang.Object") && !called.getName().equals("")) { 56 | logger.trace("Adding call graph: {}", call.toString()); 57 | GraphHolder.addCallGraph(call); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/cli/CliArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.cli; 18 | 19 | import com.google.gson.Gson; 20 | import java.io.File; 21 | import java.io.IOException; 22 | import net.nandgr.cba.custom.model.Rules; 23 | import net.nandgr.cba.exception.BadArgumentsException; 24 | import net.nandgr.cba.exception.BadRulesException; 25 | import net.nandgr.cba.logging.LogHelper; 26 | import org.apache.commons.cli.Option; 27 | import org.apache.commons.cli.Options; 28 | import org.apache.commons.cli.ParseException; 29 | import org.apache.commons.io.FileUtils; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | public class CliArguments { 34 | 35 | private static final Logger logger = LoggerFactory.getLogger(CliArguments.class); 36 | 37 | private static Options options = new Options(); 38 | public static final String MAX_THREADS_DEFAULT = "1"; 39 | public static final String MAX_ITEMS_IN_REPORT_DEFAULT = "50"; 40 | public static final String OUTPUT_DEFAULT = "report"; 41 | 42 | private CliArguments() { 43 | throw new IllegalAccessError("Cannot instantiate this utility class."); 44 | } 45 | 46 | static { 47 | // -h 48 | Option help = Option 49 | .builder("h") 50 | .longOpt("help") 51 | .desc("Print this message.") 52 | .build(); 53 | // -a 54 | Option path = Option 55 | .builder("a") 56 | .longOpt("analyze") 57 | .required() 58 | .desc("Path of the directory to run the analysis.") 59 | .hasArg() 60 | .argName("pathToAnalyze") 61 | .build(); 62 | // -t - disabled 63 | Option maxThreads = Option 64 | .builder("t") 65 | .longOpt("max-threads") 66 | .hasArg() 67 | .argName("maxThreads") 68 | .desc("Max number of threads to run the analysis. Default: " + MAX_THREADS_DEFAULT + ".") 69 | .build(); 70 | // -i 71 | Option maxReportItems = Option 72 | .builder("i") 73 | .longOpt("items-report") 74 | .hasArg() 75 | .argName("maxItems") 76 | .desc("Max number of items per report. If the number of issues found exceeds this value, the report will be split into different files. Useful if expecting too many issues in the report. Default: " + MAX_ITEMS_IN_REPORT_DEFAULT + ".") 77 | .build(); 78 | // -o 79 | Option output = Option 80 | .builder("o") 81 | .longOpt("output") 82 | .hasArg() 83 | .argName("outputDir") 84 | .desc("Directory to save the report. Warning - if there are already saved reports in this directory they will be overwritten. Default is \"" + OUTPUT_DEFAULT + "\".") 85 | .build(); 86 | // -c 87 | Option checks = Option 88 | .builder("c") 89 | .longOpt("checks") 90 | .hasArgs() 91 | .numberOfArgs(Option.UNLIMITED_VALUES) 92 | .argName("checks...") 93 | .desc("Space separated list of custom checks that are going to be run in the analysis.") 94 | .build(); 95 | // -f 96 | Option customFile = Option 97 | .builder("f") 98 | .longOpt("custom-file") 99 | .hasArg() 100 | .argName("customFile") 101 | .desc("Specify a file in JSON format to run custom rules. Read more in https://github.com/fergarrui/custom-bytecode-analyzer.") 102 | .build(); 103 | 104 | Option verboseDebug = Option 105 | .builder("v") 106 | .longOpt("verbose-debug") 107 | .desc("Increase verbosity to debug mode.") 108 | .build(); 109 | 110 | Option verboseTrace = Option 111 | .builder("vv") 112 | .longOpt("verbose-trace") 113 | .desc("Increase verbosity to trace mode - makes it slower, use it only when you need.") 114 | .build(); 115 | 116 | options.addOption(help); 117 | options.addOption(path); 118 | /* 119 | disabled until thread safety is done properly. 120 | options.addOption(maxThreads); 121 | */ 122 | options.addOption(maxReportItems); 123 | options.addOption(output); 124 | options.addOption(checks); 125 | options.addOption(customFile); 126 | options.addOption(verboseDebug); 127 | options.addOption(verboseTrace); 128 | } 129 | 130 | public static Options getOptions() { 131 | return options; 132 | } 133 | 134 | public static void parseArguments(String[] args) throws BadArgumentsException { 135 | try { 136 | CliHelper.parseCliArguments(args); 137 | } catch (ParseException e) { 138 | throw new BadArgumentsException("Error while parsing arguments", e); 139 | } 140 | 141 | CliArguments.validateArguments(); 142 | 143 | if (CliHelper.hasHelp()) { 144 | CliHelper.printHelp(); 145 | System.exit(1); 146 | } 147 | if (CliHelper.hasVerboseDebug()) { 148 | logger.info("Setting logger to DEBUG mode."); 149 | LogHelper.toDebug(); 150 | } 151 | if (CliHelper.hasVerboseTrace()) { 152 | logger.info("Setting logger to TRACE mode."); 153 | LogHelper.toTrace(); 154 | } 155 | if (CliHelper.hasCustomFile()) { 156 | String customFilePath = CliHelper.getCustomFile(); 157 | File customFile = new File(customFilePath); 158 | String json = null; 159 | try { 160 | json = FileUtils.readFileToString(customFile); 161 | } catch (IOException e) { 162 | throw new BadArgumentsException("Error with provided custom file", e); 163 | } 164 | Gson gson = new Gson(); 165 | Rules rules = gson.fromJson(json, Rules.class); 166 | try { 167 | rules.validateRules(); 168 | } catch (BadRulesException e) { 169 | throw new BadArgumentsException("Error when validating custom rules", e); 170 | } 171 | CliHelper.setRules(rules); 172 | } 173 | } 174 | 175 | public static void validateArguments() throws BadArgumentsException { 176 | if (CliHelper.hasMaxItemsInReport()) { 177 | try { 178 | int number = CliHelper.getMaxItemsInReport(); 179 | if (number < 1) { 180 | throw new BadArgumentsException("Argument \"-i\" cannot be lower than 1."); 181 | } 182 | } catch (NumberFormatException e) { 183 | throw new BadArgumentsException("Error while validating parameter \"-i\".", e); 184 | } 185 | } 186 | if (!CliHelper.hasCustomFile() && !CliHelper.hasChecks() && !CliHelper.hasHelp()) { 187 | throw new BadArgumentsException("\"-c\" or \"-f\" arguments should be provided."); 188 | } 189 | if (CliHelper.hasVerboseDebug() && CliHelper.hasVerboseTrace()) { 190 | throw new BadArgumentsException("\"-v\" and \"-vv\" arguments cannot be together. Choose one of them."); 191 | } 192 | } 193 | 194 | public static void badArgument(Exception e) { 195 | if (e.getCause() == null) { 196 | logger.error("Bad arguments ", e); 197 | } else { 198 | logger.error("Bad arguments : {}", e.getCause()); 199 | } 200 | CliHelper.printHelp(); 201 | System.exit(1); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/cli/CliHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.cli; 18 | 19 | import net.nandgr.cba.custom.model.Rules; 20 | import org.apache.commons.cli.CommandLine; 21 | import org.apache.commons.cli.CommandLineParser; 22 | import org.apache.commons.cli.DefaultParser; 23 | import org.apache.commons.cli.HelpFormatter; 24 | import org.apache.commons.cli.ParseException; 25 | 26 | public class CliHelper { 27 | 28 | private static CommandLineParser commandLineParser = new DefaultParser(); 29 | private static CommandLine commandLine; 30 | private static Rules rules; 31 | 32 | private CliHelper() { 33 | throw new IllegalAccessError("Cannot instantiate this utility class."); 34 | } 35 | 36 | public static void parseCliArguments(String[] args) throws ParseException { 37 | commandLine = commandLineParser.parse(CliArguments.getOptions(), args); 38 | } 39 | 40 | public static String getPathToAnalyze() { 41 | return commandLine.getOptionValue("a"); 42 | } 43 | 44 | public static int getMaxThreads() { 45 | return Integer.parseInt(commandLine.getOptionValue("t", CliArguments.MAX_THREADS_DEFAULT)); 46 | } 47 | 48 | public static boolean hasOutputDir() { 49 | return commandLine.hasOption("o"); 50 | } 51 | 52 | public static String getOutputDir() { 53 | return commandLine.getOptionValue("o", CliArguments.OUTPUT_DEFAULT); 54 | } 55 | 56 | public static boolean hasMaxItemsInReport() { 57 | return commandLine.hasOption("i"); 58 | } 59 | 60 | public static int getMaxItemsInReport() { 61 | return Integer.parseInt(commandLine.getOptionValue("i", CliArguments.MAX_ITEMS_IN_REPORT_DEFAULT)); 62 | } 63 | 64 | public static boolean hasChecks() { 65 | return commandLine.hasOption("c"); 66 | } 67 | 68 | public static String[] getChecks() { 69 | return commandLine.getOptionValues("c"); 70 | } 71 | 72 | public static boolean hasCustomFile() { 73 | return commandLine.hasOption("f"); 74 | } 75 | 76 | public static String getCustomFile() { 77 | return commandLine.getOptionValue("f"); 78 | } 79 | 80 | public static void printHelp() { 81 | HelpFormatter helpFormatter = new HelpFormatter(); 82 | helpFormatter.printHelp("java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE", CliArguments.getOptions()); 83 | } 84 | 85 | public static boolean hasHelp() { 86 | return commandLine.hasOption("h"); 87 | } 88 | 89 | public static boolean hasVerboseDebug() { 90 | return commandLine.hasOption("v"); 91 | } 92 | 93 | public static boolean hasVerboseTrace() { 94 | return commandLine.hasOption("vv"); 95 | } 96 | 97 | public static Rules getRules() { 98 | return rules; 99 | } 100 | 101 | public static void setRules(Rules rules) { 102 | CliHelper.rules = rules; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Annotation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | public class Annotation { 20 | 21 | private String type; 22 | private Boolean report; 23 | 24 | public String getType() { 25 | return type; 26 | } 27 | 28 | public void setType(String type) { 29 | this.type = type; 30 | } 31 | 32 | public Boolean isReport() { 33 | if (report == null) { 34 | return true; 35 | } 36 | return report; 37 | } 38 | 39 | public void setReport(Boolean report) { 40 | this.report = report; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "Annotation{" + 46 | "type=" + type + 47 | ", report=" + report + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Field.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | public class Field { 20 | 21 | private String visibility; 22 | private String type; 23 | private String valueRegex; 24 | private String nameRegex; 25 | private Boolean report; 26 | 27 | public String getVisibility() { 28 | return visibility; 29 | } 30 | 31 | public void setVisibility(String visibility) { 32 | this.visibility = visibility; 33 | } 34 | 35 | public String getType() { 36 | return type; 37 | } 38 | 39 | public void setType(String type) { 40 | this.type = type; 41 | } 42 | 43 | public String getValueRegex() { 44 | return valueRegex; 45 | } 46 | 47 | public void setValueRegex(String valueRegex) { 48 | this.valueRegex = valueRegex; 49 | } 50 | 51 | public String getNameRegex() { 52 | return nameRegex; 53 | } 54 | 55 | public void setNameRegex(String nameRegex) { 56 | this.nameRegex = nameRegex; 57 | } 58 | 59 | public Boolean isReport() { 60 | if (report == null) { 61 | return true; 62 | } 63 | return report; 64 | } 65 | 66 | public void setReport(Boolean report) { 67 | this.report = report; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "Field{" + 73 | "visibility='" + visibility + '\'' + 74 | ", type='" + type + '\'' + 75 | ", valueRegex='" + valueRegex + '\'' + 76 | ", nameRegex='" + nameRegex + '\'' + 77 | ", report=" + report + 78 | '}'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Invocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | public class Invocation { 20 | 21 | private String owner; 22 | private Method method; 23 | private Method notFrom; 24 | private Method from; 25 | private Boolean report; 26 | 27 | public String getOwner() { 28 | return owner; 29 | } 30 | 31 | public void setOwner(String owner) { 32 | this.owner = owner; 33 | } 34 | 35 | public Method getMethod() { 36 | return method; 37 | } 38 | 39 | public void setMethod(Method method) { 40 | this.method = method; 41 | } 42 | 43 | public Method getNotFrom() { 44 | return notFrom; 45 | } 46 | 47 | public void setNotFrom(Method notFrom) { 48 | this.notFrom = notFrom; 49 | } 50 | 51 | public Method getFrom() { 52 | return from; 53 | } 54 | 55 | public void setFrom(Method from) { 56 | this.from = from; 57 | } 58 | 59 | public Boolean isReport() { 60 | if (report == null) { 61 | return true; 62 | } 63 | return report; 64 | } 65 | 66 | public void setReport(Boolean report) { 67 | this.report = report; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "Invocation{" + 73 | "owner='" + owner + '\'' + 74 | ", method=" + method + 75 | ", notFrom=" + notFrom + 76 | ", report=" + report + 77 | '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Method.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | import java.util.List; 20 | 21 | public class Method { 22 | 23 | private String name; 24 | private String visibility; 25 | private List parameters; 26 | private List variables; 27 | private List annotations; 28 | private Boolean report; 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public String getVisibility() { 39 | return visibility; 40 | } 41 | 42 | public void setVisibility(String visibility) { 43 | this.visibility = visibility; 44 | } 45 | 46 | public List getParameters() { 47 | return parameters; 48 | } 49 | 50 | public void setParameters(List parameters) { 51 | this.parameters = parameters; 52 | } 53 | 54 | 55 | public List getAnnotations() { 56 | return annotations; 57 | } 58 | 59 | public void setAnnotations(List annotations) { 60 | this.annotations = annotations; 61 | } 62 | 63 | public List getVariables() { 64 | return variables; 65 | } 66 | 67 | public void setVariables(List variables) { 68 | this.variables = variables; 69 | } 70 | 71 | public Boolean isReport() { 72 | if (report == null) { 73 | return true; 74 | } 75 | return report; 76 | } 77 | 78 | public void setReport(Boolean report) { 79 | this.report = report; 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return "Method{" + 85 | "name='" + name + '\'' + 86 | ", visibility='" + visibility + '\'' + 87 | ", parameters=" + parameters + 88 | ", variables=" + variables + 89 | ", annotations=" + annotations + 90 | ", report=" + report + 91 | '}'; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Parameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | import java.util.List; 20 | 21 | public class Parameter { 22 | 23 | private String type; 24 | private List annotations; 25 | private Boolean report; 26 | 27 | public String getType() { 28 | return type; 29 | } 30 | 31 | public List getAnnotations() { 32 | return annotations; 33 | } 34 | 35 | public Boolean isReport() { 36 | if (report == null) { 37 | return true; 38 | } 39 | return report; 40 | } 41 | 42 | public void setType(String type) { 43 | this.type = type; 44 | } 45 | 46 | public void setAnnotations(List annotations) { 47 | this.annotations = annotations; 48 | } 49 | 50 | public Boolean getReport() { 51 | return report; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "Parameter{" + 57 | "type='" + type + '\'' + 58 | ", annotations=" + annotations + 59 | ", report=" + report + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Rule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | import java.util.List; 20 | 21 | public class Rule { 22 | 23 | private String name; 24 | private List interfaces; 25 | private String superClass; 26 | private List annotations; 27 | private List fields; 28 | private List methods; 29 | private List invocations; 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public List getInterfaces() { 40 | return interfaces; 41 | } 42 | 43 | public void setInterfaces(List interfaces) { 44 | this.interfaces = interfaces; 45 | } 46 | 47 | public String getSuperClass() { 48 | return superClass; 49 | } 50 | 51 | public void setSuperClass(String superClass) { 52 | this.superClass = superClass; 53 | } 54 | 55 | public List getInvocations() { 56 | return invocations; 57 | } 58 | 59 | public void setInvocations(List invocations) { 60 | this.invocations = invocations; 61 | } 62 | 63 | public List getMethods() { 64 | return methods; 65 | } 66 | 67 | public void setMethods(List methods) { 68 | this.methods = methods; 69 | } 70 | 71 | public List getAnnotations() { 72 | return annotations; 73 | } 74 | 75 | public void setAnnotations(List annotations) { 76 | this.annotations = annotations; 77 | } 78 | 79 | public List getFields() { 80 | return fields; 81 | } 82 | 83 | public void setFields(List fields) { 84 | this.fields = fields; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "Rule{" + 90 | "name='" + name + '\'' + 91 | ", interfaces=" + interfaces + 92 | ", superClass='" + superClass + '\'' + 93 | ", annotations=" + annotations + 94 | ", fields=" + fields + 95 | ", methods=" + methods + 96 | ", invocations=" + invocations + 97 | '}'; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Rules.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import net.nandgr.cba.exception.BadRulesException; 22 | import org.apache.commons.lang.StringUtils; 23 | 24 | import static net.nandgr.cba.custom.visitor.helper.RuleHelper.allEmpty; 25 | 26 | public class Rules { 27 | 28 | private List rules; 29 | 30 | public List getRules() { 31 | return rules; 32 | } 33 | 34 | public void setRules(List rules) { 35 | this.rules = rules; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "Rules{" + 41 | "rules=" + Arrays.toString(rules.toArray()) + 42 | '}'; 43 | } 44 | 45 | public void validateRules() throws BadRulesException { 46 | for (Rule rule : this.getRules()) { 47 | if (StringUtils.isBlank(rule.getName())) { 48 | throw new BadRulesException("Rule name cannot be blank."); 49 | } 50 | validateRule(rule); 51 | } 52 | } 53 | 54 | private static void validateRule(Rule rule) throws BadRulesException { 55 | if (rule.getInvocations() != null) { 56 | for (Invocation invocation : rule.getInvocations()) { 57 | 58 | Method notFrom = invocation.getNotFrom(); 59 | checkEmpty(notFrom, "\"invocation.notFrom\" property cannot have all the fields blank."); 60 | 61 | Method from = invocation.getFrom(); 62 | checkEmpty(from, "\"invocation.from\" property cannot have all the fields blank."); 63 | 64 | Method method = invocation.getMethod(); 65 | checkEmpty(method, "\"invocation.method\" property cannot have all the fields blank."); 66 | 67 | if (notFrom != null && from != null) { 68 | throw new BadRulesException("\"invocation.notFrom\" and \"invocation.from\" cannot be defined at the same time in the same rule."); 69 | } 70 | } 71 | } 72 | } 73 | 74 | private static void checkEmpty(Method method, String message) throws BadRulesException { 75 | if (allEmpty(method)) { 76 | throw new BadRulesException(message); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/model/Variable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.model; 18 | 19 | import java.util.List; 20 | 21 | public class Variable { 22 | 23 | private String type; 24 | private String nameRegex; 25 | private List annotations; 26 | private Boolean report; 27 | 28 | public String getType() { 29 | return type; 30 | } 31 | 32 | public void setType(String type) { 33 | this.type = type; 34 | } 35 | 36 | public String getNameRegex() { 37 | return nameRegex; 38 | } 39 | 40 | public void setNameRegex(String nameRegex) { 41 | this.nameRegex = nameRegex; 42 | } 43 | 44 | public List getAnnotations() { 45 | return annotations; 46 | } 47 | 48 | public void setAnnotations(List annotations) { 49 | this.annotations = annotations; 50 | } 51 | 52 | public Boolean isReport() { 53 | if (report == null) { 54 | return true; 55 | } 56 | return report; 57 | } 58 | 59 | public void setReport(Boolean report) { 60 | this.report = report; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "Variable{" + 66 | "type='" + type + '\'' + 67 | ", nameRegex='" + nameRegex + '\'' + 68 | ", annotations=" + annotations + 69 | ", report=" + report + 70 | '}'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomClassAnnotationVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import net.nandgr.cba.custom.model.Annotation; 22 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 23 | import net.nandgr.cba.custom.visitor.helper.StringsHelper; 24 | import net.nandgr.cba.report.ReportItem; 25 | import org.objectweb.asm.tree.AnnotationNode; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | public class CustomClassAnnotationVisitor extends CustomAbstractClassVisitor { 30 | 31 | private static final Logger logger = LoggerFactory.getLogger(CustomClassAnnotationVisitor.class); 32 | 33 | private final Annotation annotation; 34 | 35 | public CustomClassAnnotationVisitor(Annotation annotation, String ruleName) { 36 | super(ruleName); 37 | this.annotation = annotation; 38 | } 39 | 40 | @Override 41 | public void process() { 42 | boolean issueFound; 43 | List allAnnotations = new ArrayList<>(); 44 | List visibleAnnotations = getClassNode().visibleAnnotations; 45 | if (visibleAnnotations != null) { 46 | allAnnotations.addAll(visibleAnnotations); 47 | } 48 | List invisibleAnnotations = getClassNode().invisibleAnnotations; 49 | if (invisibleAnnotations != null) { 50 | allAnnotations.addAll(invisibleAnnotations); 51 | } 52 | for (AnnotationNode annotationNode : allAnnotations) { 53 | String desc = annotationNode.desc; 54 | boolean visible = visibleAnnotations == null ? false : visibleAnnotations.contains(annotationNode); 55 | logger.trace("visitAnnotation: desc={}, visible={}", desc, visible); 56 | issueFound = StringsHelper.simpleDescriptorToHuman(desc).equals(StringsHelper.dotsToSlashes(annotation.getType())); 57 | if (issueFound) { 58 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()); 59 | this.itemsFound().add(reportItem); 60 | this.setIssueFound(true); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public boolean showInReport() { 67 | return annotation.isReport(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomClassInterfacesVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 21 | import net.nandgr.cba.custom.visitor.helper.StringsHelper; 22 | import net.nandgr.cba.report.ReportItem; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | public class CustomClassInterfacesVisitor extends CustomAbstractClassVisitor { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(CustomClassInterfacesVisitor.class); 29 | private final List ruleInterfaces; 30 | 31 | public CustomClassInterfacesVisitor(List ruleInterfaces, String ruleName) { 32 | super(ruleName); 33 | this.ruleInterfaces = ruleInterfaces; 34 | } 35 | 36 | @Override 37 | public void process() { 38 | int version = getClassNode().version; 39 | int access = getClassNode().access; 40 | String name = getClassNode().name; 41 | String signature = getClassNode().signature; 42 | String superName = getClassNode().superName; 43 | List interfaces = getClassNode().interfaces; 44 | logger.trace("visit class: version={} access={} name={} signature={} superName={} interfaces={}", version, access, name, signature, superName, interfaces); 45 | 46 | if (validInterfaces(interfaces, ruleInterfaces)) { 47 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()); 48 | this.setIssueFound(true); 49 | this.itemsFound().add(reportItem); 50 | } 51 | } 52 | 53 | @Override 54 | public boolean showInReport() { 55 | return true; 56 | } 57 | 58 | private boolean validInterfaces(List visitorInterfaces, List ruleInterfaces) { 59 | boolean valid = true; 60 | for (String ruleInterface : ruleInterfaces) { 61 | valid &= visitorInterfaces.contains(StringsHelper.dotsToSlashes(ruleInterface)); 62 | } 63 | return valid; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomClassSuperClassVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 21 | import net.nandgr.cba.custom.visitor.helper.StringsHelper; 22 | import net.nandgr.cba.report.ReportItem; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | public class CustomClassSuperClassVisitor extends CustomAbstractClassVisitor { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(CustomClassSuperClassVisitor.class); 29 | private final String superClass; 30 | 31 | public CustomClassSuperClassVisitor(String superClass, String ruleName) { 32 | super(ruleName); 33 | this.superClass = superClass; 34 | } 35 | 36 | @Override 37 | public void process() { 38 | int version = getClassNode().version; 39 | int access = getClassNode().access; 40 | String name = getClassNode().name; 41 | String signature = getClassNode().signature; 42 | String superName = getClassNode().superName; 43 | List interfaces = getClassNode().interfaces; 44 | logger.trace("visit class: version={} access={} name={} signature={} superName={} interfaces={}", version, access, name, signature, superName, interfaces); 45 | 46 | if (superName.equals(StringsHelper.dotsToSlashes(superClass))) { 47 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()); 48 | this.setIssueFound(true); 49 | this.itemsFound().add(reportItem); 50 | } 51 | } 52 | 53 | @Override 54 | public boolean showInReport() { 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomFieldVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import net.nandgr.cba.custom.model.Field; 20 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 21 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 22 | import net.nandgr.cba.report.ReportItem; 23 | import org.objectweb.asm.tree.FieldNode; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | public class CustomFieldVisitor extends CustomAbstractClassVisitor { 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(CustomFieldVisitor.class); 30 | private final Field field; 31 | 32 | public CustomFieldVisitor(Field field, String ruleName) { 33 | super(ruleName); 34 | this.field = field; 35 | } 36 | 37 | @Override 38 | public void process() { 39 | boolean issueFound; 40 | for (FieldNode fieldNode : getClassNode().fields) { 41 | int access = fieldNode.access; 42 | String name = fieldNode.name; 43 | String desc = fieldNode.desc; 44 | String signature = fieldNode.signature; 45 | Object value = fieldNode.value; 46 | logger.trace("visitField: access={}, name={}, desc={}, signature={}, value={}", access, name, desc, signature, value); 47 | issueFound = RuleHelper.isValidField(field, access, name, desc, signature, value); 48 | if (issueFound) { 49 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()).addProperty("Field name", name); 50 | itemsFound().add(reportItem); 51 | setIssueFound(issueFound); 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | public boolean showInReport() { 58 | return field.isReport(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomInvocationFinderInsnVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import net.nandgr.cba.custom.visitor.base.CustomAbstractMethodInsnVisitor; 20 | import net.nandgr.cba.report.ReportItem; 21 | import net.nandgr.cba.custom.model.Invocation; 22 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 23 | import org.apache.commons.lang.StringEscapeUtils; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | public class CustomInvocationFinderInsnVisitor extends CustomAbstractMethodInsnVisitor { 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(CustomInvocationFinderInsnVisitor.class); 30 | private final Invocation methodInvocation; 31 | private final String calledFrom; 32 | 33 | public CustomInvocationFinderInsnVisitor(String calledFrom, Invocation methodInvocation, String ruleName) { 34 | super(ruleName); 35 | this.calledFrom = calledFrom; 36 | this.methodInvocation = methodInvocation; 37 | } 38 | 39 | @Override 40 | public void process() { 41 | String name = getMethodInsnNode().name; 42 | String owner = getMethodInsnNode().owner; 43 | String desc = getMethodInsnNode().desc; 44 | logger.trace("visitMethodInsn: owner={} name={} desc={}", owner, name, desc); 45 | if (RuleHelper.isValidMethodInvocation(methodInvocation, owner, name, desc)) { 46 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()) 47 | .addProperty("Method Name", calledFrom) 48 | .addProperty("Found method name", StringEscapeUtils.escapeHtml(name)); 49 | 50 | itemsFound().add(reportItem); 51 | setIssueFound(true); 52 | } 53 | } 54 | 55 | @Override 56 | public boolean showInReport() { 57 | return methodInvocation.isReport(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomMethodInvocationVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.model.Invocation; 21 | import net.nandgr.cba.custom.model.Method; 22 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 23 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 24 | import org.objectweb.asm.tree.AbstractInsnNode; 25 | import org.objectweb.asm.tree.InsnList; 26 | import org.objectweb.asm.tree.MethodInsnNode; 27 | import org.objectweb.asm.tree.MethodNode; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | public class CustomMethodInvocationVisitor extends CustomAbstractClassVisitor { 32 | 33 | private final Invocation invocation; 34 | 35 | private static final Logger logger = LoggerFactory.getLogger(CustomMethodInvocationVisitor.class); 36 | 37 | public CustomMethodInvocationVisitor(Invocation invocation, String ruleName) { 38 | super(ruleName); 39 | this.invocation = invocation; 40 | } 41 | 42 | @Override 43 | public void process() { 44 | for (MethodNode methodNode : getClassNode().methods) { 45 | InsnList instructions = methodNode.instructions; 46 | for (int i = 0; i < instructions.size(); i++) { 47 | AbstractInsnNode insnNode = instructions.get(i); 48 | if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) { 49 | MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; 50 | processMethodInsnNode(methodNode, methodInsnNode); 51 | } 52 | } 53 | } 54 | } 55 | 56 | private void processMethodInsnNode(MethodNode methodNode, MethodInsnNode methodInsnNode) { 57 | int access = methodNode.access; 58 | String name = methodNode.name; 59 | String desc = methodNode.desc; 60 | String signature = methodNode.signature; 61 | List exceptions = methodNode.exceptions; 62 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", access, name, desc, signature, exceptions); 63 | 64 | Method notFrom = invocation.getNotFrom(); 65 | Method from = invocation.getFrom(); 66 | processAndReport(methodInsnNode, access, name, notFrom, from); 67 | } 68 | 69 | private void processAndReport(MethodInsnNode methodInsnNode, int access, String name, Method notFrom, Method from) { 70 | if (RuleHelper.checkNotFrom(notFrom, access, name) && RuleHelper.checkFrom(from, access, name)) { 71 | CustomInvocationFinderInsnVisitor customInvocationFinderInsnVisitor = new CustomInvocationFinderInsnVisitor(name, invocation, getRuleName()); 72 | customInvocationFinderInsnVisitor.setNode(methodInsnNode); 73 | customInvocationFinderInsnVisitor.process(); 74 | if (customInvocationFinderInsnVisitor.issueFound()) { 75 | itemsFound().addAll(customInvocationFinderInsnVisitor.itemsFound()); 76 | setIssueFound(true); 77 | } 78 | } 79 | } 80 | 81 | @Override 82 | public boolean showInReport() { 83 | return invocation.isReport(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/CustomMethodVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import net.nandgr.cba.custom.model.Annotation; 22 | import net.nandgr.cba.custom.model.Method; 23 | import net.nandgr.cba.custom.model.Parameter; 24 | import net.nandgr.cba.custom.model.Variable; 25 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 26 | import net.nandgr.cba.report.ReportItem; 27 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 28 | import org.objectweb.asm.tree.AnnotationNode; 29 | import org.objectweb.asm.tree.LocalVariableAnnotationNode; 30 | import org.objectweb.asm.tree.MethodNode; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | public class CustomMethodVisitor extends CustomAbstractClassVisitor { 35 | 36 | private static final Logger logger = LoggerFactory.getLogger(CustomMethodVisitor.class); 37 | private final Method method; 38 | 39 | public CustomMethodVisitor(Method method, String ruleName) { 40 | super(ruleName); 41 | this.method = method; 42 | } 43 | 44 | @Override 45 | public void process() { 46 | for (MethodNode methodNode : getClassNode().methods) { 47 | int access = methodNode.access; 48 | String name = methodNode.name; 49 | String desc = methodNode.desc; 50 | String signature = methodNode.signature; 51 | List exceptions = methodNode.exceptions; 52 | 53 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", access, name, desc, signature, exceptions); 54 | List annotations = method.getAnnotations(); 55 | boolean annotationFound = true; 56 | if (annotations != null && !annotations.isEmpty()) { 57 | annotationFound = isMethodAnnotationFound(annotations, methodNode); 58 | } 59 | List variables = method.getVariables(); 60 | boolean variableFound = true; 61 | boolean variableAnnotationFound = true; 62 | if (variables != null && !variables.isEmpty()) { 63 | for (Variable ruleVariable : variables) { 64 | variableFound &= RuleHelper.containsVariable(ruleVariable, methodNode.localVariables); 65 | List variableAnnotations = ruleVariable.getAnnotations(); 66 | if (variableAnnotations !=null && !variableAnnotations.isEmpty()) { 67 | variableAnnotationFound &= isLocalVariableAnnotationFound(variableAnnotations, methodNode); 68 | } 69 | } 70 | } 71 | boolean parameterFound = true; 72 | boolean parameterAnnotationFound = true; 73 | List parameters = method.getParameters(); 74 | if (parameters != null && !parameters.isEmpty()) { 75 | for (Parameter parameterRule : parameters) { 76 | parameterFound &= RuleHelper.containsParameter(parameterRule, desc); 77 | List parameterAnnotations = parameterRule.getAnnotations(); 78 | parameterAnnotationFound &= isParameterAnnotationFound(parameterAnnotations, methodNode); 79 | } 80 | } 81 | 82 | if (RuleHelper.isValidMethod(method, access, name) && annotationFound && variableFound && parameterFound && variableAnnotationFound && parameterAnnotationFound) { 83 | ReportItem reportItem = new ReportItem(getRuleName(), showInReport()).addProperty("Method Name", name); 84 | this.itemsFound.add(reportItem); 85 | logger.debug("Issue found at method - access: {}, name: {}, desc: {}, signature: {}, exceptions: {}", access, name, desc, signature, exceptions); 86 | this.foundIssue = true; 87 | } 88 | } 89 | } 90 | 91 | private boolean isParameterAnnotationFound(List annotationRules, MethodNode methodNode) { 92 | boolean annotationFound = true; 93 | List allParameterAnnotations = new ArrayList<>(); 94 | List[] visibleParameterAnnotations = methodNode.visibleParameterAnnotations; 95 | if (visibleParameterAnnotations != null && visibleParameterAnnotations.length != 0) { 96 | addIfNotNull(allParameterAnnotations, visibleParameterAnnotations); 97 | } 98 | List[] inVisibleParameterAnnotations = methodNode.invisibleParameterAnnotations; 99 | if (inVisibleParameterAnnotations != null && inVisibleParameterAnnotations.length != 0) { 100 | addIfNotNull(allParameterAnnotations, inVisibleParameterAnnotations); 101 | } 102 | if (annotationRules != null && !annotationRules.isEmpty()) { 103 | for (Annotation annotationRule : annotationRules) { 104 | annotationFound &= RuleHelper.containsAnnotation(annotationRule, allParameterAnnotations); 105 | } 106 | } 107 | 108 | return annotationFound; 109 | } 110 | 111 | private void addIfNotNull(List allParameterAnnotations, List[] visibleParameterAnnotations) { 112 | for (List visibleAnnotations : visibleParameterAnnotations) { 113 | if (visibleAnnotations != null) { 114 | allParameterAnnotations.addAll(visibleAnnotations); 115 | } 116 | } 117 | } 118 | 119 | private boolean isLocalVariableAnnotationFound(List annotationRules, MethodNode methodNode) { 120 | boolean annotationFound = true; 121 | List allLocalVariableAnnotations = new ArrayList<>(); 122 | List invisibleVariableAnnotations = methodNode.invisibleLocalVariableAnnotations; 123 | if (invisibleVariableAnnotations != null) { 124 | allLocalVariableAnnotations.addAll(invisibleVariableAnnotations); 125 | } 126 | List visibleVariableAnnotations = methodNode.visibleLocalVariableAnnotations; 127 | if (visibleVariableAnnotations != null) { 128 | allLocalVariableAnnotations.addAll(visibleVariableAnnotations); 129 | } 130 | for (Annotation annotationRule : annotationRules) { 131 | annotationFound &= RuleHelper.containsAnnotation(annotationRule, allLocalVariableAnnotations); 132 | } 133 | return annotationFound; 134 | } 135 | 136 | private boolean isMethodAnnotationFound(List annotationRules, MethodNode methodNode) { 137 | boolean annotationFound = true; 138 | List allMethodAnnotations = new ArrayList<>(); 139 | List invisibleAnnotations = methodNode.invisibleAnnotations; 140 | if (invisibleAnnotations != null) { 141 | allMethodAnnotations.addAll(invisibleAnnotations); 142 | } 143 | List visibleAnnotations = methodNode.visibleAnnotations; 144 | if (visibleAnnotations != null) { 145 | allMethodAnnotations.addAll(visibleAnnotations); 146 | } 147 | for (Annotation annotationRule : annotationRules) { 148 | annotationFound &= RuleHelper.containsAnnotation(annotationRule, allMethodAnnotations); 149 | } 150 | return annotationFound; 151 | } 152 | 153 | @Override 154 | public boolean showInReport() { 155 | return method.isReport(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/RuleVisitorsAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor; 18 | 19 | import net.nandgr.cba.custom.visitor.base.CustomVisitor; 20 | import net.nandgr.cba.report.ReportItem; 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import org.objectweb.asm.tree.ClassNode; 25 | 26 | public class RuleVisitorsAnalyzer { 27 | 28 | private final List visitorList = new ArrayList<>(); 29 | 30 | public List getVisitorList() { 31 | return visitorList; 32 | } 33 | 34 | public List runRules(ClassNode classNode) throws IOException { 35 | boolean meetsAllVisitors = true; 36 | List reportItems = new ArrayList<>(); 37 | 38 | 39 | for (CustomVisitor customVisitor : visitorList) { 40 | customVisitor.setNode(classNode); 41 | customVisitor.process(); 42 | meetsAllVisitors &= customVisitor.issueFound(); 43 | if (!meetsAllVisitors) { 44 | return new ArrayList<>(); 45 | } 46 | for (ReportItem reportItem : customVisitor.itemsFound()) { 47 | if (reportItem.isShowInReport()) { 48 | reportItems.add(reportItem); 49 | } 50 | } 51 | customVisitor.clear(); 52 | } 53 | return reportItems; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/base/CustomAbstractClassVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.base; 18 | 19 | import org.objectweb.asm.tree.ClassNode; 20 | 21 | public abstract class CustomAbstractClassVisitor extends CustomAbstractVisitor { 22 | 23 | private ClassNode classNode; 24 | 25 | public CustomAbstractClassVisitor(String ruleName) { 26 | super(ruleName); 27 | } 28 | 29 | @Override 30 | public void setNode(Object node) { 31 | this.classNode = (ClassNode) node; 32 | } 33 | 34 | public ClassNode getClassNode() { 35 | return classNode; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/base/CustomAbstractMethodInsnVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.base; 18 | 19 | import org.objectweb.asm.tree.MethodInsnNode; 20 | 21 | public abstract class CustomAbstractMethodInsnVisitor extends CustomAbstractVisitor { 22 | 23 | private MethodInsnNode methodInsnNode; 24 | 25 | public CustomAbstractMethodInsnVisitor(String ruleName) { 26 | super(ruleName); 27 | } 28 | 29 | public MethodInsnNode getMethodInsnNode() { 30 | return methodInsnNode; 31 | } 32 | 33 | @Override 34 | public void setNode(Object node) { 35 | this.methodInsnNode = (MethodInsnNode) node; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/base/CustomAbstractVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.base; 18 | 19 | import java.util.Collection; 20 | import java.util.HashSet; 21 | import net.nandgr.cba.custom.visitor.base.CustomVisitor; 22 | import net.nandgr.cba.report.ReportItem; 23 | import java.util.ArrayList; 24 | 25 | public abstract class CustomAbstractVisitor implements CustomVisitor { 26 | 27 | protected Collection itemsFound = new HashSet<>(); 28 | protected boolean foundIssue = false; 29 | private final String ruleName; 30 | 31 | public CustomAbstractVisitor(String ruleName) { 32 | this.ruleName = ruleName; 33 | } 34 | 35 | @Override 36 | public boolean issueFound() { 37 | return foundIssue; 38 | } 39 | 40 | @Override 41 | public void setIssueFound(boolean issueFound) { 42 | this.foundIssue = issueFound; 43 | } 44 | 45 | @Override 46 | public Collection itemsFound() { 47 | return itemsFound; 48 | } 49 | 50 | @Override 51 | public String getRuleName() { 52 | return this.ruleName; 53 | } 54 | 55 | @Override 56 | public void clear() { 57 | this.itemsFound.clear(); 58 | this.foundIssue = false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/base/CustomVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.base; 18 | 19 | import java.util.Collection; 20 | import net.nandgr.cba.report.ReportItem; 21 | 22 | public interface CustomVisitor { 23 | void setNode(Object node); 24 | void process(); 25 | boolean issueFound(); 26 | void setIssueFound(boolean issueFound); 27 | Collection itemsFound(); 28 | String getRuleName(); 29 | boolean showInReport(); 30 | void clear(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/helper/RuleHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.helper; 18 | 19 | import java.util.List; 20 | import java.util.regex.Matcher; 21 | import java.util.regex.Pattern; 22 | import java.util.regex.PatternSyntaxException; 23 | import net.nandgr.cba.custom.model.Annotation; 24 | import net.nandgr.cba.custom.model.Field; 25 | import net.nandgr.cba.custom.model.Invocation; 26 | import net.nandgr.cba.custom.model.Method; 27 | import net.nandgr.cba.custom.model.Parameter; 28 | import net.nandgr.cba.custom.model.Variable; 29 | import org.apache.commons.lang.StringUtils; 30 | import org.objectweb.asm.Opcodes; 31 | import org.objectweb.asm.Type; 32 | import org.objectweb.asm.tree.AnnotationNode; 33 | import org.objectweb.asm.tree.LocalVariableNode; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | public class RuleHelper { 38 | 39 | private static final Logger logger = LoggerFactory.getLogger(RuleHelper.class); 40 | 41 | private RuleHelper() { 42 | throw new IllegalAccessError("Cannot instantiate this utility class."); 43 | } 44 | 45 | 46 | public static boolean allEmpty(Method method) { 47 | if (method == null) { 48 | return false; 49 | } 50 | return StringUtils.isBlank(method.getName()) && method.getParameters() == null && StringUtils.isBlank(method.getVisibility()); 51 | } 52 | 53 | public static boolean isValidMethod(Method method, int access, String name) { 54 | boolean isValid = true; 55 | String methodVisibility = method.getVisibility(); 56 | if (methodVisibility != null) { 57 | int ruleVisibility = getOpcodeVisibility(methodVisibility); 58 | isValid &= (access & ruleVisibility) != 0; 59 | } 60 | String methodName = method.getName(); 61 | if (methodName != null) { 62 | isValid &= methodName.equals(name); 63 | } 64 | logger.trace("isValidMethod: {} - method={}, access={}, name={}", isValid, method, access, name ); 65 | return isValid; 66 | } 67 | 68 | public static boolean isValidMethodInvocation(Invocation invocation, String owner, String name, String desc) { 69 | boolean isValid = true; 70 | Method invocationMethod = invocation.getMethod(); 71 | if (invocationMethod != null) { 72 | isValid &= isValidMethod(invocationMethod, 0, name ); 73 | } 74 | String invocationOwner = invocation.getOwner(); 75 | if (invocationOwner != null) { 76 | isValid &= owner.equals(StringsHelper.dotsToSlashes(invocationOwner)); 77 | } 78 | logger.trace("isValidMethodInvocation: {} - invocation={}, owner={}, name={}, desc={}", isValid, invocation, owner, name, desc); 79 | return isValid; 80 | } 81 | 82 | public static boolean isValidField(Field field, int access, String name, String desc, String signature, Object value) { 83 | logger.trace("isValidField: field={}, access={}, name={}, desc={}, signature={}, value={}", field, access, name, desc, signature, value); 84 | boolean isValid = true; 85 | String type = field.getType(); 86 | if (!StringUtils.isBlank(type)) { 87 | isValid &= StringsHelper.dotsToSlashes(type).equals(StringsHelper.simpleDescriptorToHuman(desc)); 88 | } 89 | String visibility = field.getVisibility(); 90 | if (!StringUtils.isBlank(visibility)) { 91 | int visibilityOpcode = getOpcodeVisibility(visibility); 92 | isValid &= (access & visibilityOpcode) != 0; 93 | } 94 | String valueRegex = field.getValueRegex(); 95 | if (!StringUtils.isBlank(valueRegex)) { 96 | try { 97 | Pattern pattern = Pattern.compile(valueRegex); 98 | String valueString = String.valueOf(value); 99 | Matcher matcher = pattern.matcher(valueString); 100 | isValid &= matcher.matches(); 101 | } catch (PatternSyntaxException e) { 102 | throw e; 103 | } 104 | } 105 | String nameRegex = field.getNameRegex(); 106 | if (!StringUtils.isBlank(nameRegex)) { 107 | try { 108 | Pattern pattern = Pattern.compile(nameRegex); 109 | String nameString = String.valueOf(name); 110 | Matcher matcher = pattern.matcher(nameString); 111 | isValid &= matcher.matches(); 112 | } catch (PatternSyntaxException e) { 113 | throw e; 114 | } 115 | } 116 | logger.trace("isValidField : {} - field={}, access={}, name={}, desc={}, signature={}, value={}", isValid, field, access, name, desc, signature, value); 117 | return isValid; 118 | } 119 | 120 | public static boolean containsAnnotation(Annotation annotationRule, List annotationNodes) { 121 | for (AnnotationNode annotationNode : annotationNodes) { 122 | String desc = annotationNode.desc; 123 | if (StringsHelper.simpleDescriptorToHuman(desc).equals(StringsHelper.dotsToSlashes(annotationRule.getType()))) { 124 | return true; 125 | } 126 | } 127 | return false; 128 | } 129 | 130 | public static boolean containsParameter(Parameter parameterRule, String desc) { 131 | Type[] parameters = Type.getArgumentTypes(desc); 132 | logger.trace("containsParameter: parameterRule={}, desc={}"); 133 | if (parameters == null) { 134 | return false; 135 | } 136 | for (Type parameter : parameters) { 137 | logger.trace("parameter={}", parameter.getClassName()); 138 | if(parameterRule.getType() == null || parameterRule.getType().equals(parameter.getClassName())) { 139 | return true; 140 | } 141 | } 142 | return false; 143 | } 144 | 145 | public static boolean containsVariable(Variable variableRule, List variableNodes) { 146 | logger.trace("containsVariable: variableRule={}, variableNodes={}", variableRule, variableNodes); 147 | if (variableNodes == null) { 148 | return false; 149 | } 150 | for (LocalVariableNode variableNode : variableNodes) { 151 | String name = variableNode.name; 152 | String desc = variableNode.desc; 153 | if (!name.contains("this") && isValidVariable(variableRule, name, desc)) { 154 | return true; 155 | } 156 | } 157 | return false; 158 | } 159 | 160 | public static boolean isValidVariable(Variable variable, String name, String desc) { 161 | logger.trace("isValidVariable : variable={}, name={}, desc={}", variable, name, desc); 162 | boolean isValid = true; 163 | String type = variable.getType(); 164 | if (!StringUtils.isBlank(type)) { 165 | isValid &= desc.contains(StringsHelper.dotsToSlashes(variable.getType())); 166 | } 167 | String nameRegex = variable.getNameRegex(); 168 | if (!StringUtils.isBlank(nameRegex)) { 169 | try { 170 | Pattern pattern = Pattern.compile(nameRegex); 171 | Matcher matcher = pattern.matcher(name); 172 | isValid &= matcher.matches(); 173 | } catch (PatternSyntaxException e) { 174 | throw e; 175 | } 176 | } 177 | return isValid; 178 | } 179 | 180 | public static int getOpcodeVisibility(String visibility) { 181 | if ("public".equals(visibility)) { 182 | return Opcodes.ACC_PUBLIC; 183 | } 184 | if ("protected".equals(visibility)) { 185 | return Opcodes.ACC_PROTECTED; 186 | } 187 | if ("private".equals(visibility)) { 188 | return Opcodes.ACC_PRIVATE; 189 | } 190 | return 0; 191 | } 192 | 193 | public static boolean checkNotFrom(Method notFrom, int access, String name) { 194 | if (notFrom == null) { 195 | return true; 196 | } 197 | return !RuleHelper.isValidMethod(notFrom, access, name); 198 | } 199 | 200 | public static boolean checkFrom(Method from, int access, String name) { 201 | if (from == null) { 202 | return true; 203 | } 204 | return RuleHelper.isValidMethod(from, access, name); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/custom/visitor/helper/StringsHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.custom.visitor.helper; 18 | 19 | public class StringsHelper { 20 | 21 | private static final String CLASS_SUFFIX = ".class"; 22 | 23 | private StringsHelper() { 24 | throw new IllegalAccessError("Cannot instantiate this utility class."); 25 | } 26 | 27 | public static String dotsToSlashes(String s) { 28 | return s.replaceAll("\\.", "/"); 29 | } 30 | 31 | public static String spacesToDashesLowercase(String s) { 32 | return s.replaceAll(" ", "-").toLowerCase(); 33 | } 34 | 35 | public static String simpleDescriptorToHuman(String s) { 36 | String result = s; 37 | if(result.startsWith("[")) { 38 | result = result.substring(1, result.length()); 39 | } 40 | if (result.startsWith("L")) { 41 | result = result.substring(1, result.length()); 42 | } 43 | if (result.endsWith(";")) { 44 | result = result.substring(0, result.length()-1); 45 | 46 | } 47 | return result; 48 | } 49 | 50 | public static String removeClassSuffix(String s) { 51 | if (s.endsWith(CLASS_SUFFIX)) { 52 | return s.substring(0, s.length()-CLASS_SUFFIX.length()); 53 | } 54 | return s; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/decompile/Decompiler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.decompile; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | 22 | public interface Decompiler { 23 | String decompile(InputStream inputStream, String entryName) throws IOException; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/decompile/ZipEntryDecompiler.java: -------------------------------------------------------------------------------- 1 | package net.nandgr.cba.decompile; 2 | 3 | import com.strobel.decompiler.PlainTextOutput; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.PrintWriter; 10 | import java.io.StringWriter; 11 | import net.nandgr.cba.cli.CliHelper; 12 | import org.apache.commons.io.FileUtils; 13 | import org.apache.commons.io.IOUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | public class ZipEntryDecompiler implements Decompiler { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(ZipEntryDecompiler.class); 20 | private static final String CLASS_SUFFIX = ".class"; 21 | private static final String DECOMPILED_DIR = "decompiled/"; 22 | 23 | @Override 24 | public String decompile(InputStream inputStream, String entryName) throws IOException { 25 | logger.debug("Decompiling... {}", entryName); 26 | File tempFile = createTempFile(entryName, inputStream); 27 | tempFile.deleteOnExit(); 28 | 29 | String decompiledFileName = getDecompiledFileName(entryName); 30 | File decompiledFile = new File(decompiledFileName); 31 | decompiledFile.getParentFile().mkdirs(); 32 | 33 | StringWriter pw = new StringWriter(); 34 | try { 35 | com.strobel.decompiler.Decompiler.decompile(tempFile.getAbsolutePath(), new PlainTextOutput(pw)); 36 | } catch (Exception e) { 37 | logger.info("Error while decompiling {}. " , entryName); 38 | throw e; 39 | } 40 | pw.flush(); 41 | 42 | String decompiledFileContent = pw.toString(); 43 | FileUtils.writeStringToFile(decompiledFile, decompiledFileContent); 44 | return decompiledFileContent; 45 | } 46 | 47 | private static File createTempFile(String entryName, InputStream inputStream) throws IOException { 48 | byte[] inputStreamBytes = IOUtils.toByteArray(inputStream); 49 | InputStream byteArrayInputStream = new ByteArrayInputStream(inputStreamBytes); 50 | 51 | String prefix = entryName.replaceAll(File.separator, "."); 52 | final File tempFile = File.createTempFile(prefix, ".class"); 53 | FileOutputStream outputStream = new FileOutputStream(tempFile); 54 | int copiedBytes = IOUtils.copy(byteArrayInputStream, outputStream); 55 | logger.debug("Copied {} bytes to a temp file.", copiedBytes); 56 | return tempFile; 57 | } 58 | 59 | private static String getDecompiledFileName(String entryName) { 60 | String outputDir = CliHelper.getOutputDir(); 61 | if (!outputDir.endsWith(File.separator)) { 62 | outputDir += File.separator; 63 | } 64 | outputDir += DECOMPILED_DIR; 65 | String zipEntryName = entryName; 66 | if (zipEntryName.endsWith(CLASS_SUFFIX)) { 67 | zipEntryName = zipEntryName.substring(0, zipEntryName.length() - CLASS_SUFFIX.length()); 68 | } 69 | return outputDir + zipEntryName + ".java"; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/exception/BadArgumentsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.exception; 18 | 19 | public class BadArgumentsException extends Exception { 20 | 21 | public BadArgumentsException(String message) { 22 | super(message); 23 | } 24 | 25 | public BadArgumentsException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/exception/BadRulesException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.exception; 18 | 19 | public class BadRulesException extends Exception { 20 | 21 | public BadRulesException(String message) { 22 | super(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/logging/LogHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.logging; 18 | 19 | import ch.qos.logback.classic.Level; 20 | import ch.qos.logback.classic.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | public class LogHelper { 24 | 25 | private LogHelper() { 26 | throw new IllegalAccessError("Cannot instantiate this utility class."); 27 | } 28 | public static void toDebug() { 29 | Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 30 | root.setLevel(Level.DEBUG); 31 | } 32 | 33 | public static void toTrace() { 34 | Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 35 | root.setLevel(Level.TRACE); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/report/ReportBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.report; 18 | 19 | import com.google.common.collect.Lists; 20 | import java.io.StringWriter; 21 | import java.util.Properties; 22 | import net.nandgr.cba.custom.visitor.helper.StringsHelper; 23 | import net.nandgr.cba.cli.CliHelper; 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Map; 29 | import org.apache.commons.io.FileUtils; 30 | import org.apache.velocity.Template; 31 | import org.apache.velocity.VelocityContext; 32 | import org.apache.velocity.app.VelocityEngine; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | public class ReportBuilder { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(ReportBuilder.class); 39 | 40 | private ReportBuilder() { 41 | throw new IllegalAccessError("Do not instantiate this class."); 42 | } 43 | 44 | public static void saveAsHtml(Map> reportItems) { 45 | for (Map.Entry> entry : reportItems.entrySet()) { 46 | String ruleName = entry.getKey(); 47 | saveAsHtml(ruleName,entry.getValue()); 48 | } 49 | } 50 | 51 | public static void saveAsHtml(String ruleName, List reportItemList) { 52 | File reportsDirectory = new File(CliHelper.getOutputDir()); 53 | if (!reportsDirectory.exists()) { 54 | reportsDirectory.mkdir(); 55 | } 56 | int reportFileIndex = 0; 57 | try { 58 | List htmlChunks = generateHtmlChunks(reportItemList); 59 | for (String htmlChunk : htmlChunks) { 60 | File reportFile = new File(reportsDirectory.getAbsolutePath() + "/" + StringsHelper.spacesToDashesLowercase(ruleName) + "-"+ reportFileIndex +".html"); 61 | reportFile.createNewFile(); 62 | FileUtils.writeStringToFile(reportFile, htmlChunk); 63 | reportFileIndex++; 64 | } 65 | logger.info(reportItemList.size() + " issue(s) found for \"" + ruleName + "\". Report created in: " + reportsDirectory.getAbsolutePath()); 66 | } catch (IOException e) { 67 | logger.error("Error when saving the report.", e); 68 | } 69 | } 70 | 71 | private static List generateHtmlChunks(List reportItemList) { 72 | List htmlChunks = new ArrayList<>(); 73 | 74 | VelocityEngine velocityEngine = new VelocityEngine(); 75 | Properties p = new Properties(); 76 | p.setProperty("resource.loader", "class"); 77 | p.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); 78 | velocityEngine.init(p); 79 | Template template = velocityEngine.getTemplate("template/report_template.html"); 80 | 81 | int maxItemsInReport = CliHelper.getMaxItemsInReport(); 82 | List> reportItemsChunks = Lists.partition(reportItemList, maxItemsInReport); 83 | 84 | for (List reportItemsChunk : reportItemsChunks ) { 85 | VelocityContext velocityContext = new VelocityContext(); 86 | velocityContext.put("jarPath", CliHelper.getPathToAnalyze()); 87 | velocityContext.put("ruleName", reportItemsChunk.get(0).getRuleName()); 88 | velocityContext.put("reportItems", reportItemsChunk); 89 | 90 | StringWriter stringWriter = new StringWriter(); 91 | template.merge(velocityContext, stringWriter); 92 | htmlChunks.add(stringWriter.toString()); 93 | } 94 | return htmlChunks; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/report/ReportItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.report; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import org.apache.commons.lang.StringEscapeUtils; 22 | 23 | public class ReportItem { 24 | 25 | private String jarPath = ""; 26 | private String className = ""; 27 | private String decompiledFile; 28 | private final String ruleName; 29 | private final boolean showInReport; 30 | private Map properties = new HashMap<>(); 31 | 32 | public ReportItem(String ruleName, boolean showInReport) { 33 | this.ruleName = ruleName; 34 | this.showInReport = showInReport; 35 | } 36 | 37 | public String getJarPath() { 38 | return jarPath; 39 | } 40 | 41 | public String getClassName() { 42 | return className; 43 | } 44 | 45 | public String getRuleName() { 46 | return ruleName; 47 | } 48 | 49 | public void setJarPath(String jarPath) { 50 | this.jarPath = jarPath; 51 | } 52 | 53 | public void setClassName(String className) { 54 | this.className = className; 55 | } 56 | 57 | public boolean isShowInReport() { 58 | return showInReport; 59 | } 60 | 61 | public String getDecompiledFile() { 62 | return decompiledFile; 63 | } 64 | 65 | public String getDecompiledFileHtml() { 66 | return StringEscapeUtils.escapeHtml(decompiledFile); 67 | } 68 | 69 | public Map getProperties() { 70 | return properties; 71 | } 72 | 73 | public ReportItem addProperty(String propertyName, String propertyValue) { 74 | properties.put(propertyName, propertyValue); 75 | return this; 76 | } 77 | 78 | public void setDecompiledFile(String decompiledFile) { 79 | this.decompiledFile = decompiledFile; 80 | } 81 | 82 | @Override 83 | public boolean equals(Object o) { 84 | if (this == o) return true; 85 | if (!(o instanceof ReportItem)) return false; 86 | 87 | ReportItem that = (ReportItem) o; 88 | 89 | if (isShowInReport() != that.isShowInReport()) return false; 90 | if (getJarPath() != null ? !getJarPath().equals(that.getJarPath()) : that.getJarPath() != null) return false; 91 | if (getClassName() != null ? !getClassName().equals(that.getClassName()) : that.getClassName() != null) 92 | return false; 93 | if (getDecompiledFile() != null ? !getDecompiledFile().equals(that.getDecompiledFile()) : that.getDecompiledFile() != null) 94 | return false; 95 | if (getRuleName() != null ? !getRuleName().equals(that.getRuleName()) : that.getRuleName() != null) return false; 96 | return getProperties() != null ? getProperties().equals(that.getProperties()) : that.getProperties() == null; 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | int result = getJarPath() != null ? getJarPath().hashCode() : 0; 102 | result = 31 * result + (getClassName() != null ? getClassName().hashCode() : 0); 103 | result = 31 * result + (getDecompiledFile() != null ? getDecompiledFile().hashCode() : 0); 104 | result = 31 * result + (getRuleName() != null ? getRuleName().hashCode() : 0); 105 | result = 31 * result + (isShowInReport() ? 1 : 0); 106 | result = 31 * result + (getProperties() != null ? getProperties().hashCode() : 0); 107 | return result; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/visitor/checks/CustomDeserializationCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.visitor.checks; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 21 | import net.nandgr.cba.report.ReportItem; 22 | import net.nandgr.cba.visitor.checks.util.SerializationHelper; 23 | import org.objectweb.asm.tree.MethodNode; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | public class CustomDeserializationCheck extends CustomAbstractClassVisitor { 28 | 29 | private static final Logger logger = LoggerFactory.getLogger(CustomDeserializationCheck.class); 30 | 31 | public CustomDeserializationCheck() { 32 | super("CustomDeserializationCheck"); 33 | } 34 | 35 | @Override 36 | public void process() { 37 | for (MethodNode methodNode : getClassNode().methods) { 38 | int access = methodNode.access; 39 | String name = methodNode.name; 40 | String desc = methodNode.name; 41 | String signature = methodNode.signature; 42 | List exceptions = methodNode.exceptions; 43 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", access, name, desc, signature, exceptions); 44 | if (SerializationHelper.isCustomDeserializationMethod(access, name, desc)) { 45 | ReportItem reportItem = new ReportItem(getRuleName(), foundIssue).addProperty("Method name", name); 46 | this.foundIssue = true; 47 | this.itemsFound.add(reportItem); 48 | logger.debug("Issue found at method - access: {}, name: {}, desc: {}, signature: {}, exceptions: {}", access, name, desc, signature, exceptions); 49 | } 50 | } 51 | } 52 | 53 | @Override 54 | public boolean showInReport() { 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/visitor/checks/DeserializationCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.visitor.checks; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.model.Invocation; 21 | import net.nandgr.cba.custom.model.Method; 22 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 23 | import net.nandgr.cba.custom.visitor.CustomInvocationFinderInsnVisitor; 24 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 25 | import net.nandgr.cba.visitor.checks.util.SerializationHelper; 26 | import org.objectweb.asm.tree.AbstractInsnNode; 27 | import org.objectweb.asm.tree.InsnList; 28 | import org.objectweb.asm.tree.MethodInsnNode; 29 | import org.objectweb.asm.tree.MethodNode; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | public class DeserializationCheck extends CustomAbstractClassVisitor { 34 | 35 | private static final Logger logger = LoggerFactory.getLogger(DeserializationCheck.class); 36 | 37 | private static final String READ_OBJECT_OWNER = "java/io/ObjectInputStream"; 38 | private static final String READ_OBJECT_NAME = "readObject"; 39 | 40 | private final Invocation invocation; 41 | 42 | public DeserializationCheck() { 43 | super("DeserializationCheck"); 44 | Invocation invocation = new Invocation(); 45 | invocation.setOwner(READ_OBJECT_OWNER); 46 | Method method = new Method(); 47 | method.setName(READ_OBJECT_NAME); 48 | invocation.setMethod(method); 49 | this.invocation = invocation; 50 | } 51 | 52 | @Override 53 | public void process() { 54 | for (MethodNode method : getClassNode().methods) { 55 | int methodAccess = method.access; 56 | String methodName = method.name; 57 | String methodDesc = method.desc; 58 | String methodSignature = method.signature; 59 | List methodExceptions = method.exceptions; 60 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", methodAccess, methodName, methodDesc, methodSignature, methodExceptions); 61 | if (SerializationHelper.isCustomDeserializationMethod(methodAccess, methodName, methodDesc)) { 62 | InsnList instructions = method.instructions; 63 | for (int i = 0; i < instructions.size(); i++) { 64 | AbstractInsnNode abstractInsnNode = instructions.get(i); 65 | if (abstractInsnNode.getType() == AbstractInsnNode.METHOD_INSN) { 66 | MethodInsnNode methodInsnNode = (MethodInsnNode) abstractInsnNode; 67 | int access = method.access; 68 | String name = method.name; 69 | String desc = method.desc; 70 | String signature = method.signature; 71 | List exceptions = method.exceptions; 72 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", access, name, desc, signature, exceptions); 73 | 74 | Method notFrom = invocation.getNotFrom(); 75 | Method from = invocation.getFrom(); 76 | if (RuleHelper.checkNotFrom(notFrom, access, name) && RuleHelper.checkFrom(from, access, name)) { 77 | CustomInvocationFinderInsnVisitor customInvocationFinderInsnVisitor = new CustomInvocationFinderInsnVisitor(name, invocation, getRuleName()); 78 | customInvocationFinderInsnVisitor.setNode(methodInsnNode); 79 | customInvocationFinderInsnVisitor.process(); 80 | if (customInvocationFinderInsnVisitor.issueFound()) { 81 | itemsFound().addAll(customInvocationFinderInsnVisitor.itemsFound()); 82 | setIssueFound(true); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | @Override 92 | public boolean showInReport() { 93 | return true; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/visitor/checks/InvokeMethodCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.visitor.checks; 18 | 19 | import java.util.List; 20 | import net.nandgr.cba.custom.model.Invocation; 21 | import net.nandgr.cba.custom.model.Method; 22 | import net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor; 23 | import net.nandgr.cba.custom.visitor.CustomInvocationFinderInsnVisitor; 24 | import net.nandgr.cba.custom.visitor.helper.RuleHelper; 25 | import org.objectweb.asm.tree.AbstractInsnNode; 26 | import org.objectweb.asm.tree.InsnList; 27 | import org.objectweb.asm.tree.MethodInsnNode; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | public class InvokeMethodCheck extends CustomAbstractClassVisitor { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(InvokeMethodCheck.class); 34 | 35 | private static final String INVOKE_METHOD_OWNER = "java/lang/reflect/Method"; 36 | private static final String INVOKE_METHOD_NAME = "invoke"; 37 | 38 | private final Invocation invocation; 39 | 40 | public InvokeMethodCheck() { 41 | super("InvokeMethodCheck"); 42 | Invocation invocation = new Invocation(); 43 | invocation.setOwner(INVOKE_METHOD_OWNER); 44 | Method method = new Method(); 45 | method.setName(INVOKE_METHOD_NAME); 46 | invocation.setMethod(method); 47 | this.invocation = invocation; 48 | } 49 | 50 | @Override 51 | public void process() { 52 | getClassNode().methods.stream().forEach(method -> { 53 | InsnList instructions = method.instructions; 54 | for (int i = 0; i < instructions.size(); i++) { 55 | AbstractInsnNode abstractInsnNode = instructions.get(i); 56 | if (abstractInsnNode.getType() == AbstractInsnNode.METHOD_INSN) { 57 | MethodInsnNode methodInsnNode = (MethodInsnNode) abstractInsnNode; 58 | int access = method.access; 59 | String name = method.name; 60 | String desc = method.desc; 61 | String signature = method.signature; 62 | List exceptions = method.exceptions; 63 | logger.trace("visitMethod: access={} name={} desc={} signature={} exceptions={}", access, name, desc, signature, exceptions); 64 | 65 | Method notFrom = invocation.getNotFrom(); 66 | Method from = invocation.getFrom(); 67 | if (RuleHelper.checkNotFrom(notFrom, access, name) && RuleHelper.checkFrom(from, access, name)) { 68 | CustomInvocationFinderInsnVisitor customInvocationFinderInsnVisitor = new CustomInvocationFinderInsnVisitor(name, invocation, getRuleName()); 69 | customInvocationFinderInsnVisitor.setNode(methodInsnNode); 70 | customInvocationFinderInsnVisitor.process(); 71 | if (customInvocationFinderInsnVisitor.issueFound()) { 72 | itemsFound().addAll(customInvocationFinderInsnVisitor.itemsFound()); 73 | setIssueFound(true); 74 | } 75 | } 76 | } 77 | } 78 | 79 | }); 80 | } 81 | 82 | @Override 83 | public boolean showInReport() { 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/net/nandgr/cba/visitor/checks/util/SerializationHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017, Fernando Garcia 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package net.nandgr.cba.visitor.checks.util; 18 | 19 | import org.objectweb.asm.Opcodes; 20 | 21 | public class SerializationHelper { 22 | 23 | private static final String READ_OBJECT_ARGUMENT = "java/io/ObjectInputStream"; 24 | private static final String READ_OBJECT_NAME = "readObject"; 25 | 26 | private SerializationHelper() { 27 | throw new IllegalAccessError("Cannot instantiate this utility class."); 28 | } 29 | 30 | public static boolean isCustomDeserializationMethod(int access, String name, String desc) { 31 | return access == Opcodes.ACC_PRIVATE && READ_OBJECT_NAME.equals(name) && desc.contains(READ_OBJECT_ARGUMENT); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cba.log 5 | true 6 | 7 | %d{HH:mm:ss.SSS} [%class{36}] %-5level - %msg%n 8 | 9 | 10 | 11 | 12 | %d{HH:mm:ss} %msg%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/template/report_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $jarPath 6 | 7 | 8 | 9 |

Report for rule "$ruleName" for path: $jarPath

10 |
11 | #foreach( $report in $reportItems) 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | #foreach( $prop in $report.properties.entrySet()) 22 | 23 | 26 | 29 | 30 | #end 31 |
Jar name: $report.jarPath
Class name: $report.className
24 | $prop.key : 25 | 27 | $prop.value 28 |
32 |
$report.getDecompiledFileHtml()
33 | #end 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /src/test/java/rules/AbstractTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import com.google.gson.Gson; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.net.URL; 8 | import java.nio.file.Files; 9 | import java.nio.file.Paths; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.stream.Collectors; 15 | import net.nandgr.cba.ByteCodeAnalyzer; 16 | import net.nandgr.cba.CustomByteCodeAnalyzer; 17 | import net.nandgr.cba.ArchiveAnalyzerCallable; 18 | import net.nandgr.cba.custom.model.Rules; 19 | import net.nandgr.cba.report.ReportItem; 20 | import org.apache.commons.io.IOUtils; 21 | import org.objectweb.asm.ClassReader; 22 | import org.objectweb.asm.tree.ClassNode; 23 | 24 | public abstract class AbstractTest { 25 | 26 | private final String RELATIVE_PATH = "testfiles"; 27 | private final String RESOURCES_PATH = "/rulejson"; 28 | private final String PATH; 29 | private List reportItems = new ArrayList<>(); 30 | // Refactor to a cli argument maybe? 31 | private final int GRAPH_MAX_THREADS = 20; 32 | private final ExecutorService graphExecutorService = Executors.newFixedThreadPool(GRAPH_MAX_THREADS); 33 | 34 | public AbstractTest(String path) { 35 | this.PATH = path; 36 | } 37 | 38 | protected void runTests() { 39 | this.reportItems.clear(); 40 | try { 41 | Rules rules = getRules(); 42 | ByteCodeAnalyzer byteCodeAnalyzer = new CustomByteCodeAnalyzer(true, false, rules); 43 | Files.walk(Paths.get(getPath())) 44 | .filter(filePath -> filePath.toUri().toString().endsWith(".class")) 45 | .forEach(filePath -> { 46 | try { 47 | InputStream inputStream = Files.newInputStream(filePath); 48 | ClassReader classReader = new ClassReader(inputStream); 49 | ClassNode classNode = new ClassNode(); 50 | classReader.accept(classNode,0); 51 | List reportItems = byteCodeAnalyzer.analyze(classNode); 52 | ArchiveAnalyzerCallable.addContextToReportItems(reportItems, "test", filePath.getFileName().toString(), null); 53 | this.reportItems.addAll(reportItems); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | }); 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | protected List getReportItems() { 64 | return reportItems; 65 | } 66 | 67 | protected List getReportItems(String ruleName) { 68 | return getReportItems().stream() 69 | .filter(reportItem -> reportItem.getRuleName().equals(ruleName)) 70 | .collect(Collectors.toList()); 71 | } 72 | 73 | protected boolean matchClassName(List reportItems, String className) { 74 | return reportItems.stream(). 75 | anyMatch( r -> r.getClassName().equals(className)); 76 | } 77 | 78 | protected Rules getRules() throws IOException { 79 | String json = IOUtils.toString(this.getClass().getResourceAsStream(getJsonPath())); 80 | Gson gson = new Gson(); 81 | Rules rules = gson.fromJson(json, Rules.class); 82 | return rules; 83 | } 84 | 85 | private String getJsonPath() { 86 | return RESOURCES_PATH + File.separator + this.getClass().getSimpleName() + ".json"; 87 | } 88 | 89 | private String getPath() { 90 | URL url = getClass().getResource(RELATIVE_PATH + File.separator + PATH); 91 | return url.getPath(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/rules/AnnotationsTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import java.util.List; 4 | import net.nandgr.cba.report.ReportItem; 5 | import org.junit.Test; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class AnnotationsTest extends AbstractTest { 10 | 11 | public AnnotationsTest() { 12 | super("annotations"); 13 | } 14 | 15 | @Test 16 | public void test(){ 17 | 18 | runTests(); 19 | 20 | List reportItems1 = getReportItems("annotations1"); 21 | assertEquals(3, reportItems1.size()); 22 | assertTrue(matchClassName(reportItems1, "AnnotationsTestFile$A.class")); 23 | assertTrue(matchClassName(reportItems1, "AnnotationsTestFile$E.class")); 24 | assertTrue(matchClassName(reportItems1, "AnnotationsTestFile$G.class")); 25 | 26 | List reportItems2 = getReportItems("annotations2"); 27 | assertEquals(2, reportItems2.size()); 28 | assertTrue(matchClassName(reportItems2, "AnnotationsTestFile$A.class")); 29 | 30 | List reportItems3 = getReportItems("annotations3"); 31 | assertEquals(2, reportItems3.size()); 32 | assertTrue(matchClassName(reportItems3, "AnnotationsTestFile$G.class")); 33 | 34 | List reportItems4 = getReportItems("annotations4"); 35 | assertEquals(2, reportItems4.size()); 36 | assertTrue(matchClassName(reportItems4, "AnnotationsTestFile$D.class")); 37 | assertTrue(matchClassName(reportItems4, "AnnotationsTestFile$G.class")); 38 | 39 | List reportItems5 = getReportItems("annotations5"); 40 | assertEquals(2, reportItems5.size()); 41 | assertTrue(matchClassName(reportItems5, "AnnotationsTestFile$A.class")); 42 | assertTrue(matchClassName(reportItems5, "AnnotationsTestFile$D.class")); 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/rules/FieldsTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | import net.nandgr.cba.report.ReportItem; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertTrue; 9 | import org.junit.Test; 10 | 11 | public class FieldsTest extends AbstractTest { 12 | 13 | public FieldsTest() { 14 | super("fields"); 15 | } 16 | 17 | @Test 18 | public void test() { 19 | runTests(); 20 | 21 | List reportItems1 = getReportItems("fields1"); 22 | assertEquals(2, reportItems1.size()); 23 | assertTrue(matchClassName(reportItems1, "FieldsTestFile$C.class")); 24 | assertTrue(matchClassName(reportItems1, "FieldsTestFile$A.class")); 25 | 26 | List reportItems2 = getReportItems("fields2").stream() 27 | .filter(r ->!r.getProperties().getOrDefault("Field name", "").contains("this")) 28 | .collect(Collectors.toList()); 29 | assertEquals(8, reportItems2.size()); 30 | 31 | List reportItems3 = getReportItems("fields3"); 32 | assertEquals(1, reportItems3.size()); 33 | assertTrue(matchClassName(reportItems3, "FieldsTestFile$C.class")); 34 | 35 | List reportItems4 = getReportItems("fields4"); 36 | assertEquals(1, reportItems4.size()); 37 | assertTrue(matchClassName(reportItems4, "FieldsTestFile$B.class")); 38 | 39 | List reportItems5 = getReportItems("fields5"); 40 | assertEquals(0, reportItems5.size()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/rules/InterfacesTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import java.util.List; 4 | import net.nandgr.cba.report.ReportItem; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | import org.junit.Test; 10 | 11 | public class InterfacesTest extends AbstractTest { 12 | 13 | public InterfacesTest() { 14 | super("interfaces"); 15 | } 16 | 17 | @Test 18 | public void test() { 19 | runTests(); 20 | 21 | List reportItems1 = getReportItems("interfaces1"); 22 | assertEquals(3, reportItems1.size()); 23 | assertTrue(matchClassName(reportItems1, "InterfacesTestFile$A.class")); 24 | assertTrue(matchClassName(reportItems1, "InterfacesTestFile$D.class")); 25 | assertTrue(matchClassName(reportItems1, "InterfacesTestFile$E.class")); 26 | 27 | List reportItems2 = getReportItems("interfaces2"); 28 | assertEquals(1, reportItems2.size()); 29 | assertTrue(matchClassName(reportItems2, "InterfacesTestFile$D.class")); 30 | 31 | List reportItems3 = getReportItems("interfaces3"); 32 | assertEquals(1, reportItems3.size()); 33 | assertTrue(matchClassName(reportItems3, "InterfacesTestFile$D.class")); 34 | 35 | List reportItems4 = getReportItems("interfaces4"); 36 | assertEquals(0, reportItems4.size()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/rules/InvocationsTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import java.util.List; 4 | import net.nandgr.cba.report.ReportItem; 5 | import org.junit.Test; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class InvocationsTest extends AbstractTest { 10 | 11 | public InvocationsTest() { 12 | super("invocations"); 13 | } 14 | 15 | @Test 16 | public void test() { 17 | 18 | runTests(); 19 | 20 | List reportItems1 = getReportItems("invocations1"); 21 | assertEquals(3, reportItems1.size()); 22 | assertTrue(matchClassName(reportItems1, "InvocationsTestFile$A.class")); 23 | assertTrue(matchClassName(reportItems1, "InvocationsTestFile$B.class")); 24 | assertTrue(matchClassName(reportItems1, "InvocationsTestFile$D.class")); 25 | 26 | List reportItems2 = getReportItems("invocations2"); 27 | assertEquals(2, reportItems2.size()); 28 | assertTrue(matchClassName(reportItems2, "InvocationsTestFile$B.class")); 29 | assertTrue(matchClassName(reportItems2, "InvocationsTestFile$D.class")); 30 | 31 | List reportItems3 = getReportItems("invocations3"); 32 | assertEquals(4, reportItems3.size()); 33 | assertTrue(matchClassName(reportItems3, "InvocationsTestFile$A.class")); 34 | assertTrue(matchClassName(reportItems3, "InvocationsTestFile$B.class")); 35 | 36 | List reportItems4 = getReportItems("invocations4"); 37 | assertEquals(2, reportItems4.size()); 38 | assertTrue(matchClassName(reportItems4, "InvocationsTestFile$A.class")); 39 | assertTrue(matchClassName(reportItems4, "InvocationsTestFile$B.class")); 40 | 41 | List reportItems5 = getReportItems("invocations5"); 42 | assertEquals(1, reportItems5.size()); 43 | assertTrue(matchClassName(reportItems5, "InvocationsTestFile$B.class")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/rules/MethodsTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import java.util.List; 4 | import net.nandgr.cba.report.ReportItem; 5 | import org.junit.Test; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class MethodsTest extends AbstractTest { 10 | 11 | public MethodsTest() { 12 | super("methods"); 13 | } 14 | 15 | @Test 16 | public void test(){ 17 | 18 | runTests(); 19 | 20 | List reportItems1 = getReportItems("methods1"); 21 | assertEquals(4, reportItems1.size()); // 3 + 22 | assertTrue(matchClassName(reportItems1, "MethodsTestFile$A.class")); 23 | assertTrue(matchClassName(reportItems1, "MethodsTestFile$D.class")); 24 | assertTrue(matchClassName(reportItems1, "MethodsTestFile$E.class")); 25 | assertTrue(matchClassName(reportItems1, "MethodsTestFile.class")); 26 | 27 | List reportItems2 = getReportItems("methods2"); 28 | assertEquals(1, reportItems2.size()); 29 | assertTrue(matchClassName(reportItems2, "MethodsTestFile$B.class")); 30 | 31 | List reportItems3 = getReportItems("methods3"); 32 | assertEquals(2, reportItems3.size()); // 1 report per variable 33 | assertTrue(matchClassName(reportItems3, "MethodsTestFile$C.class")); 34 | 35 | List reportItems4 = getReportItems("methods4"); 36 | assertEquals(1, reportItems4.size()); 37 | assertTrue(matchClassName(reportItems4, "MethodsTestFile$F.class")); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/rules/SuperClassTest.java: -------------------------------------------------------------------------------- 1 | package rules; 2 | 3 | import net.nandgr.cba.report.ReportItem; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class SuperClassTest extends AbstractTest { 9 | 10 | public SuperClassTest() { 11 | super("superclass"); 12 | } 13 | 14 | @Test 15 | public void test() { 16 | runTests(); 17 | ReportItem reportItem0 = getReportItems().get(0); 18 | assertEquals("SuperClassTestFile$B.class", reportItem0.getClassName()); 19 | ReportItem reportItem1 = getReportItems().get(1); 20 | assertEquals("SuperClassTestFile$C.class", reportItem1.getClassName()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/annotations/AnnotationsTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.annotations; 2 | 3 | import com.google.gson.annotations.Since; 4 | 5 | public class AnnotationsTestFile { 6 | @Deprecated 7 | @Since(1) 8 | class A { 9 | } 10 | class B { 11 | } 12 | class C { 13 | @Deprecated 14 | void method1() { 15 | } 16 | } 17 | @Since(1) 18 | class D { 19 | @Deprecated() 20 | void method2(@Deprecated Integer a) { 21 | } 22 | } 23 | @Deprecated 24 | class E { 25 | 26 | } 27 | class F { 28 | 29 | } 30 | @Deprecated 31 | class G { 32 | @Deprecated 33 | void method(@Deprecated Integer a) { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/fields/FieldsTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.fields; 2 | 3 | public class FieldsTestFile { 4 | 5 | class A { 6 | private String a; 7 | private int b; 8 | private String password; 9 | } 10 | 11 | class B { 12 | private Integer a; 13 | private static final String b = "valueB"; 14 | } 15 | 16 | class C { 17 | public Integer a; 18 | public String password; 19 | private String passwd; 20 | } 21 | 22 | class D{ 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/interfaces/InterfacesTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.interfaces; 2 | 3 | public class InterfacesTestFile { 4 | 5 | class A implements Comparable { 6 | @Override 7 | public int compareTo(Object o) { 8 | return 0; 9 | } 10 | } 11 | interface B { 12 | } 13 | class C implements B { 14 | } 15 | class D implements Comparable, B { 16 | @Override 17 | public int compareTo(Object o) { 18 | return 0; 19 | } 20 | } 21 | class E implements Comparable, F { 22 | @Override 23 | public int compareTo(Object o) { 24 | return 0; 25 | } 26 | } 27 | interface F { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/invocations/InvocationsTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.invocations; 2 | 3 | public class InvocationsTestFile { 4 | class A { 5 | private void method() { 6 | String a = ""; 7 | System.out.println(a.toString()); 8 | Boolean b = true; 9 | System.out.println(b.toString()); 10 | boolean c = a.equals(b); 11 | } 12 | } 13 | class B { 14 | public void method3() { 15 | String a = ""; 16 | System.out.println(a.toString()); 17 | Boolean b = true; 18 | System.out.println(b.toString()); 19 | boolean c = a.equals(b); 20 | } 21 | } 22 | class C { 23 | public void method3() { 24 | String a = ""; 25 | System.out.println(a); 26 | Boolean b = true; 27 | System.out.println(b.getClass()); 28 | boolean c = a.equals(b); 29 | } 30 | } 31 | class D { 32 | public void method3() { 33 | String a = ""; 34 | System.out.println(a.toString()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/methods/MethodsTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.methods; 2 | 3 | public class MethodsTestFile { 4 | 5 | class A { 6 | public void method(String a, Integer b) { 7 | } 8 | } 9 | class B { 10 | private void method(String a, Integer b) { 11 | } 12 | } 13 | class C { 14 | private void method() { 15 | String a = new String(); 16 | Integer b = new Integer(1); 17 | String c = "c"; 18 | } 19 | } 20 | class D { 21 | public void method() { 22 | String a = new String(); 23 | Integer b = new Integer(1); 24 | String c = "c"; 25 | String passwd = "123456"; 26 | } 27 | } 28 | class E { 29 | public void method(String param) { 30 | String a = new String(); 31 | Integer b = new Integer(1); 32 | String c = "c"; 33 | String passwd = "123456"; 34 | } 35 | } 36 | class F { 37 | private void method() { 38 | String passwd = "123456"; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/rules/testfiles/superclass/SuperClassTestFile.java: -------------------------------------------------------------------------------- 1 | package rules.testfiles.superclass; 2 | 3 | import java.util.Date; 4 | 5 | public class SuperClassTestFile { 6 | class A { 7 | private String a; 8 | } 9 | class B extends Date { 10 | 11 | } 12 | class C extends A { 13 | 14 | } 15 | class D extends C { 16 | 17 | } 18 | class E { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/AnnotationsTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name" : "annotations1", 4 | "annotations" : [{ 5 | "type" : "java.lang.Deprecated" 6 | }] 7 | }, 8 | { 9 | "name" : "annotations2", 10 | "annotations" : [{ 11 | "type" : "java.lang.Deprecated" 12 | }, 13 | { 14 | "type" : "com.google.gson.annotations.Since" 15 | }] 16 | }, 17 | { 18 | "name" : "annotations3", 19 | "annotations" : [{ 20 | "type" : "java.lang.Deprecated" 21 | }], 22 | "methods" : [{ 23 | "annotations" : [{ 24 | "type" : "java.lang.Deprecated" 25 | }], 26 | "parameters" : [{ 27 | "annotations" : [{ 28 | "type" : "java.lang.Deprecated" 29 | }] 30 | }] 31 | }] 32 | }, 33 | { 34 | "name" : "annotations4", 35 | "methods" : [{ 36 | "parameters" : [{ 37 | "annotations" : [{ 38 | "type" : "java.lang.Deprecated" 39 | }] 40 | }] 41 | }] 42 | }, 43 | { 44 | "name" : "annotations5", 45 | "annotations" : [{ 46 | "type" : "com.google.gson.annotations.Since" 47 | }] 48 | }] 49 | } 50 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/FieldsTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name" : "fields1", 4 | "fields" : [ 5 | { 6 | "visibility" : "private", 7 | "type" : "java.lang.String", 8 | "nameRegex" : "(password|pass|psswd|passwd)" 9 | } 10 | ] 11 | }, 12 | { 13 | "name" : "fields2", 14 | "fields" : [ 15 | { 16 | } 17 | ] 18 | }, 19 | { 20 | "name" : "fields3", 21 | "fields" : [ 22 | { 23 | "visibility" : "public", 24 | "type" : "java.lang.String" 25 | } 26 | ] 27 | }, 28 | { 29 | "name" : "fields4", 30 | "fields" : [ 31 | { 32 | "valueRegex" : "valueB" 33 | } 34 | ] 35 | }, 36 | { 37 | "name" : "fields5", 38 | "fields" : [ 39 | { 40 | "visibility" : "private", 41 | "type" : "java.lang.Integer", 42 | "nameRegex" : "(password|pass|psswd|passwd)" 43 | } 44 | ] 45 | }] 46 | } 47 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/InterfacesTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "interfaces1", 4 | "interfaces" : ["java.lang.Comparable"] 5 | }, 6 | { 7 | "name": "interfaces2", 8 | "interfaces" : ["java.lang.Comparable", "rules.testfiles.interfaces.InterfacesTestFile$B"] 9 | }, 10 | { 11 | "name": "interfaces3", 12 | "interfaces" : ["rules.testfiles.interfaces.InterfacesTestFile$B", "java.lang.Comparable"] 13 | }, 14 | { 15 | "name": "interfaces4", 16 | "interfaces" : ["rules.testfiles.interfaces.InterfacesTestFile$F", "rules.testfiles.interfaces.InterfacesTestFile$B"] 17 | }] 18 | } 19 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/InvocationsTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "name": "invocations1", 5 | "invocations" : [{ 6 | "method" : { 7 | "name" : "toString" 8 | } 9 | }] 10 | }, 11 | { 12 | "name": "invocations2", 13 | "invocations" : [{ 14 | "method" : { 15 | "name" : "toString" 16 | }, 17 | "notFrom" : { 18 | "visibility" : "private" 19 | } 20 | }] 21 | }, 22 | { 23 | "name": "invocations3", 24 | "invocations" : [{ 25 | "method" : { 26 | "name" : "toString" 27 | } 28 | }, 29 | { 30 | "method" : { 31 | "name" : "equals" 32 | } 33 | } 34 | ] 35 | }, 36 | { 37 | "name": "invocations4", 38 | "invocations" : [{ 39 | "owner" : "java.lang.Boolean", 40 | "method" : { 41 | "name" : "toString" 42 | } 43 | }] 44 | }, 45 | { 46 | "name": "invocations5", 47 | "invocations" : [{ 48 | "owner" : "java.lang.Boolean", 49 | "method" : { 50 | "name" : "toString" 51 | }, 52 | "from" : { 53 | "name" : "method3" 54 | } 55 | }] 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/MethodsTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [ 3 | { 4 | "name": "methods1", 5 | "methods" : [{ 6 | "visibility" : "public" 7 | }] 8 | }, 9 | { 10 | "name": "methods2", 11 | "methods" : [{ 12 | "visibility" : "private", 13 | "parameters" : [{ 14 | "type" : "java.lang.String" 15 | }, 16 | { 17 | "type" : "java.lang.Integer" 18 | }] 19 | }] 20 | }, 21 | { 22 | "name": "methods3", 23 | "methods" : [{ 24 | "visibility" : "private", 25 | "variables" : [{ 26 | "type" : "java.lang.String" 27 | }, 28 | { 29 | "type" : "java.lang.Integer" 30 | }] 31 | }] 32 | }, 33 | { 34 | "name": "methods4", 35 | "methods" : [{ 36 | "visibility" : "private", 37 | "variables" : [{ 38 | "type" : "java.lang.String", 39 | "nameRegex" : "(password|pass|psswd|passwd)" 40 | }] 41 | }] 42 | }, 43 | { 44 | "name": "methods5", 45 | "methods" : [{ 46 | "visibility" : "private", 47 | "variables" : [{ 48 | "type" : "java.lang.Integer", 49 | "nameRegex" : "(password|pass|psswd|passwd)" 50 | }] 51 | }] 52 | }, 53 | { 54 | "name": "methods6", 55 | "methods" : [{ 56 | "visibility" : "private", 57 | "variables" : [{ 58 | "type" : "java.lang.Integer" 59 | }], 60 | "parameters" : [{ 61 | "type" : "java.lang.String" 62 | }] 63 | }] 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /src/test/resources/rulejson/SuperClassTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": [{ 3 | "name": "class fields test", 4 | "superClass" : "java.util.Date" 5 | }, 6 | { 7 | "name": "class fields test 2", 8 | "superClass" : "rules.testfiles.superclass.SuperClassTestFile$A" 9 | }] 10 | } 11 | --------------------------------------------------------------------------------