ENHANCING SECURE CODING ASSISTANT WITH ERROR CORRECTION AND

CONTRACT PROGRAMMING

A Project

Presented to the faculty of the Department of Computer Science

California State University, Sacramento

Submitted in partial satisfaction of the requirements for the degree of

MASTER OF SCIENCE

in

Computer Science

by

Chen Li

SPRING 2017

© 2017

Chen Li

ALL RIGHTS RESERVED

ii

ENHANCING SECURE CODING ASSISTANT WITH ERROR CORRECTION AND

CONTRACT PROGRAMMING

A Project

by

Chen Li

Approved by:

______, Committee Chair Dr. Jun Dai

______, Second Reader Dr. Cui Zhang

______Date

iii

Student: Chen Li

I certify that this student has met the requirements for format contained in the University format manual, and that this project is suitable for shelving in the Library and credit is to be awarded for the project.

______, Graduate Coordinator ______Dr. Jinsong Ouyang Date

Department of Computer Science

iv

Abstract

of

ENHANCING SECURE CODING ASSISTANT WITH ERROR CORRECTION AND

CONTRACT PROGRAMMING

by

Chen Li

As cyber-attacks have become more prevalent in the recent decade, companies and governments have learnt the significant importance of enforcing robust programming practices to ensure software security and reliability during code generation. Various tools have been developed for the purpose of assisting programmers in secure coding, and the initial version of the tool called Secure Coding Assistant is one of such development efforts.

Designed to support CERT rule violation detection, the tool is featured by providing a mechanism to detect rule violations early and by filling the void of open source tools. The tool is promising in secure programming education compared to other commercial products, however, the initial version does not provide assistance in error correction, nor does it takes into account the potentials of employing contract programming enforcement to assist users in improving program reliability. To achieve error correction and defect localization for both software security and reliability in Java programs, this project report presents our efforts for the implementations of assisting error corrections as well as enforcing contract

v

programming. The tool is maintained on GitHub at http://benw408701.github.io/Secure

CodingAssistant/.

______, Committee Chair Dr. Jun Dai

______Date

vi

ACKNOWLEDGMENTS

I am indebted to many people. First and foremost, I would like to express my everlasting gratitude and appreciation to my advisor Dr. Jun Dai for his continuous help, patience and guidance in academic aspects. I would also like to thank my second reader Dr.

Cui Zhang who brought up the idea and made this project possible. Dr. Dai and Dr. Zhang are great advisor and educator. Their inclusive thinking and scintillating ideas are critical to my graduate research. They are also great friends and are always ready to help me. The rest of my life will benefit from the experience and knowledge gained with them.

I also thank Ben White, the former graduate student who developed the initial version of Secure Coding Assistant, for mentoring me, teaching me valuable techniques, sharing experience with me when I began my project and giving me great suggestions for my project.

I would like to thank my whole family and my friends for their love and support. They are my inspiration and my motive to move forward. Without them, I would have never finish this.

Finally, acknowledgements and attributions are also given to Carnegie Mellon

University and its Software Engineering Institute, as this publication incorporates portions of the “SEI CERT Oracle Coding Standard for Java” (c) 2017 Carnegie Mellon University, with special permission from its Software Engineering Institute”. Any material of Carnegie

Mellon University and/or its software engineering institute contained herein is furnished vii

on an “as-is” basis. Carnegie Mellon University makes no warranties of any kind, either expressed or implied, as to any matter including, but not limited to, warranty of fitness for purpose or merchantability, exclusivity, or results obtained from use of the material,

Carnegie Mellon University does not make any warranty of any kind with respect to freedom from patent, trademark, or copyright infringement. This publication has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering

Institute. CERT and CERT Coordination Center are registered trademarks of Carnegie

Mellon University. Java is a registered trademark of Oracle. Inc.

viii

TABLE OF CONTENTS Page

Acknowlegments...... vii

List of Tables ...... xi

List of Figures ...... xii

Chapter

1. INTRODUCTION ...... 1

2. RELATED WORK ...... 4

2.1 Existing Tools Support Detection of Security Vulnerabilities ...... 4

2.2 Existing Tools Support Contract Programming in Java ...... 5

2.2.1 Jass ...... 6

2.2.2 iContract ...... 7

2.2.3 jContractor ...... 8

2.2.4 Contract4J ...... 10

2.2.5 Cofoja ...... 11

2.2.6 Comparion of Existing Tools ...... 13

3. DESIGN...... 15

3.1 Goals...... 15

3.2 Architecture ...... 15

4. IMPLEMENTATION ...... 18

4.1 Plugin Implementations...... 18

4.2 AST and ASTWrite ...... 20 ix

4.3 “Quick Fix” Feature and Source Code Analysis ...... 20

4.4 Integration of Methodology ...... 28

4.5 “Export Contract Annotation” Feature ...... 30

4.6 “Disable/Enable Design by Contract Enforcement” Feature ...... 31

5. LIMITATION, CONCLUSION AND FUTURE WORK ...... 33

Appendix ...... 34

References ...... 125

x

LIST OF TABLES Tables Page

1. Review of static analysis tools for security vulnerabilities ...... 5

2. Existing tools for Design by Contract in Java ...... 13

xi

LIST OF FIGURES Figures Page

1. Jass example...... 6

2. iContract example ...... 7

3. Applications of "forall", "exists" and "implies" in iContract ...... 8

4. jContractor example ...... 9

5. Contract4J example ...... 10

6. Cofoja example ...... 12

7. Workflow of Secure Coding Assistant ...... 16

8. Quick fixes provided in Secure Coding Assistant ...... 19

9. Quick fix to random number generation rule violation ...... 21

10. Secure random generator quick fix results: use SecureRandom (top) or skip rule

1 check (bottom) ...... 21

11. Source code with "Do not use the Object.equals() method to compare two arrays"

1 rule violation ...... 22

12. The violated() method used to evaluate "Do not use the Object.equals() method to

1 compare two arrays" ...... 23

13. The preVisit() method in class SecureNodeAnalyzer ...... 23

14. The getSolution() method in class EXP02J_DoNotUseObjectEqualstoCompare

1 Arrays ...... 25

15. Source code for quick fixes generation ...... 27

xii

16. Quick fixes for "Do not use the Object.equals() method to compare two arrays"

1 rule violation ...... 27

17. Class global variable missing restriction ...... 29

18. Method skip pre-condition check (top) and method missing pre-condition check

1 (bottom)...... 30

19. Customized menu...... 31

20. Export contract annotation of BookStore class to text file ...... 31

21. Disable "Design by Contract Enforcement" ...... 32

xiii 1

Chapter 1

INTRODUCTION

In the recent decade, cyber-attack has become more prevalent. Based on the statistic published on Statista.com, between 2005 and 2014 millions of data records have been breached in the United Stated [1]. For example, a data breach to Heartland Payment

Systems in 2008 and 2009 resulted in 130 million records comprised [2]. JP Morgan Chase, the largest bank in the United States, was the victim of a security breach in 2014 impacting over 76 million household accounts and seven million small businesses [3]. In 2015,

Anthem Blue Cross, one of the largest health insurance companies in the US, was attacked resulting in about 78.8 million people’s personal information being stolen [4]. Had the security vulnerabilities been detected at the software development stage, the likelihood of those incidents would have been greatly reduced.

Despite of the security vulnerabilities which are exploited by hackers to compromise the system, unreliability is another contributor to poor quality of software and often leads to security loopholes. The well-known incident of Ariane 5 in 1996 was the result of poor software reliability, which led to the fatal crash of the rocket shortly after its launch.

Investigation conducted by the French Space Agency and European Space Agency pointed to an overflow error introduced in the guidance software converting a 64-bit floating-point horizontal velocity to a 16-bit signed integer. The error led to the shutdown of the guidance system and eventually caused the rocket to veer off course. The catastrophic event of

Ariane 5 cost European Space Agency 10 years of R&D effort and 7 billion dollars, making the bug the most costly error in history [5]. The disaster could have been avoided if

2 contract programming methodology had been applied to the guidance software development.

The above incidences call for “robust programming practices” to detect and correct any defects in the code, for the purposes of ensuring both software security (in case of any threats coming from hackers) and reliability (in case of any risks from non-malicious activities). The tool named “Secure Coding Assistant” is developed to address the community’s demands. The initial version [6] mainly targets at solving the insecure coding practices, by providing a mechanism based on CERT rules [7] to detect any rule violations at the stage as early as writing code. To fill the void of open source products, the tool is developed as a non-commercial contribution and released through GitHub to promote secure programming education.

However, with the focus on early defect detection, the initial version of Secure Coding

Assistant didn’t contain facilities to provide assistance in error correction. It also didn’t provide support to improve code correctness for the sake of risk mitigation in terms of software reliability, where the scenarios may not have any attackers (i.e. the vulnerability is trigger by other factors). Of course, the design rationales at that time didn’t include the potentials of contract programming, which can be integrated with rule violation in one coherent ecosystem to enforce programming practices to ensure software reliability as well as security.

A short paper [8] that presents our efforts in the current version to fill the afore-mentioned gaps, achieving the enforcement of code robustness, based on assisting error correction and contract programming has been submitted and is under review. This report is an extended

3 version of that paper. In this report, the following sections: abstract, introduction, existing tools for enforcing secure programming practices, design and limitation, conclusion and future work are reused, and the section “existing tools for contract programming in Java” is extened to provide brief introductions of those tools. In addition, analysis of the source code are given in the “Implementation” section to explain the implementations of the newly added features, including “quick fix”, “enforcement of Design by Contract”,

“disable/enable the enforcement of Design by Contract” and “contract exportation”.

This project is a follow up work of “Secure Coding Assistant: enforcing secure coding practices using the eclipse development environment” [6; 9], which implemented an open source Eclipse plugin that alerts developer of the CERT rule [7] violation. Credits and special thanks are given to White, B., et al. for their contributions to the implemation of

“Secure Coding Assistant”. In “Appendix” section, the work done by White, B., et al. is included, and to distingruish our work from theirs, the author of the source code is provided in the comment ahead of class/method declaration.

4

Chapter 2

RELATED WORK

2.1 Existing Tools Support Detection of Security Vulnerabilities

Fourteen existing static analysis tools, which were developed to support the enforcement of secure coding practices, have been reviewed by our previous work [6]. As shown in

Table 1 [8], although eight of them support early detection, others are late in defect localization, and most of them don’t provide much detail regarding vulnerabilities and the mechanisms to find them. The only two open source tools before the initial release of

Secure Coding Assistant are FindBugs [10] and ASIDE [11], with the former one lacking support in early detection of rule violation, and the latter one limited in only providing assistance in web application development.

Amongst all the tools, only ASIDE supports assistance in automatic fix [11]. However, it follows the OWASP rules and primarily focuses on detecting and fixing vulnerabilities in web application development. In contrast, the Secure Coding Assistant is more generic, following CERT rules [7] and focuses on providing solutions for any software development using Java. Table 1 is the expansion of our previous review of static analysis tools [6]. The highlighted columns compare the newly added features between those tools and our new version.

5

Table 1. Review of static analysis tools for security vulnerabilities

Early/Late Open/Close Error Tool Contract Programming Detection d Correction White Box Testing/Binary Late Closed No No Static Analysis Fortify Static Code Analyzer Late Closed No No Sentinel Source Late Closed No No Klockwork Insight Early Closed No No SecureAssist Early Closed No No Early Security Vulnerability Early Closed No No Detector (ESVD) Static Security Vulnerability Early Closed No No Analyzer Contrast for Eclipse Late Closed No No SonarLint Early Closed No No CxSuite Late Closed No No Goanna Studio Early Closed No No FindBugs Late Open No No Coverity Prevent Early Closed No No ASIDE Early Open Yes No Secure Coding Assistant Early Open Yes Yes (Current Version)

2.2 Existing Tools Support Contract Programming in Java

On the other hand, to increase the realiablilty of the code, Contract Programming methodology is developed and applied during the code generation. Contract Programming is also called Design by Contract [12]. It is a methodology that helps produce correct and reliable software by specifying the contracts in terms of pre-conditions, post-conditions for methods, and invariants for class, and by checking the contracts automatically at run time.

Eiffle [13] is the first programming language which implemented Design by Contract. Most other languages do not provide this built-in mechanism, however, much effort has been made to provide tools for supporting Design by Contract in other programming languages such as Java [14-19], C# [20] and PHP [21].

6

There are three kinds of assertions: , and . The precondition specifies the conditions that must be satisfied before the execution of a method and is evaluated before the function call. In contrast, the postcondition specifies the conditions that must be satisfied after the method execution and is evaluated after the function call. Invariant specifies the conditions that must be hold anytime for a global variable and is evaluated before and after any method is executed.

There are several tools have been developed to support Design by Contract in Java, and a brief description for each of them is given below.

2.2.1 Jass

Jass [22] stands for “Java with assertions”. It is a comment-based tool developed by Detlef

Bartezko as his master’s thesis. The tool allows developers use several keywords such as invariant, require, ensure, check, rescue, retry to define the contracts. The application of

Jass is shown in Figure 1:

Figure 1. Jass example

7

As shown above, the pre-and post-condition in Jass have to be defined inside the method, and the invariant checking should be specified at the end of the class. In addition to the standard Design by Contract features, some keywords such as “retry” and “rescue” are also introduced to handle contract violation in Jass.

2.2.2 iContract

Similar to Jass, iContract [16] uses source code comments to define the contract. It processes the original code to generate the decorated Java files which will then be compiled by the Java compiler as usual. The application of iContract is shown in Figure 2.

Figure 2. iContract example

As shown above, iContract uses keyword @inv, @pre and @post to define contract by placing them before class and method declarations. In addition, iContract also supports the use of other keywords, such as, exists, forall and implies. As shown in Figure 3, the “exists”

8 specifies that there is at least one IRoom that will be retured after calling the getRooms() function, and the “forall” means every IEmployee that is returned by calling getEmployees() function needs to meet the specified condition. The “implies” shown in the example indicates if condition A holds, then must does B.

Figure 3. Applications of "forall", "exists" and "implies" in iContract

2.2.3 jContractor jContractor [17] is brought up by Murat Karaorman, Urs Holzel, and John Bruno. This tool is a library-based system that supports Design by Contract in Java and doesn’t requires the use of modified compilers, modified JVMs, runtime systems or pre-processors. It specifies the contracts by defining additional methods, and performs contract checking using name conversion. The application of jContractor is shown in (Figure 4)

9

Figure 4. jContractor example

As shown above, the precondition, postcondition and exception for the method put() and the invariant for the class Dictionary are specified in the methods put_Precondition(), put_Postcondition(), put_OnException() and Dictionary_ClassInvariant() respectively.

Those _Pre/Postcondition, _OnException and _ClassInvariant are the predefined contract patterns that can be recognized by jContractor class loader and be rewritten as contracts for run-time checking. The definions of contract can either be written within the same class or provided in a separate class with the name of ClassName_CONTRACT, in which the

ClassName is replaced with the original class name.

10

2.2.4 Contract4J

Contract4J [19] is a tool that supports “Design by Contract” in Java by using annotation to define contracts. Unlike the comment-based tools, which contracts are not available to the runtime environement without preprocessing the code, the annotations “can be included in the generated Javadocs and the JVM can be aware of the annotations at runtime” which can be used to support other aspect behaviors [19]. As shown in Figure 5, this tool uses

@Contract annotation to inform the presence of the contracts and uses @Pre, @Post and

@Invar to define the precondition, postcondition for method and invariant for class. Upon the detection of an unsatisfied condition, this tool throws an “ContractError” exception at run time to inform the developer of contract violation.

Figure 5. Contract4J example

11

2.2.5 Cofoja

Cofoja [23] which stands for “contract for Java”, is a tool that supports “Design by Contract” in Java. This tool uses annotation processing and bytecode instrumentation to provide contract syntactic checking and sementic checking at compile time and run time respectively. The tool is developed by Nhat at Google in 2010 based on his previous work of Modern Jass. As shown in Figure 6, the contracts defined in this tool are embedded in annotations. The main annotations are @Requires, @Ensures, @ThrowEnsures and

@Invariant, which are used to specify the precondition, postcondition check for method and invariant check for class respectively. In addition, several keywords such as old and result are supported to in contract definition. For example, by using keyword old ahead of a variable, it refers the old value of the variable before the update. And the keywork result is an alias for the return variable, and can be used in postcondition definition. Upon the detection of contract violation, an exception including the reason and position of the problem, is thrown.

12

Figure 6. Cofoja example

13

2.2.6 Comparison of Existing Tools

Table 2 Existing tools for Design by Contract in Java

Tool Implementation Support Contract Syntactic Checking

Jass comment No iContract comment No jContractor contract No method Contract4J annotation No Cofoja annotation Yes

The above tools that support Design by Contract in Java are compared in Table 2 [8].

Among them, Jass and iContract are comment-based tools, jContractor uses additional methods to define contract and performs contract checking using name conversion, while

Contract4J and Cofoja specify the contract via annotation.

After evaluating those tools, Cofoja was selected for integration with the new version of

Secure Coding Assistant. This tool provides run-time checking by utilizing annotation processing and bytecode instrumentation [18]. The rationales for this selection are listed below:

 Cofoja is featured with syntactic checking, which provides instant feedback to

developers upon the detection of syntactic error in the contracts;

 The enforcement of Design by Contract methodology requires Secure Coding Assistant

to be able to analyze the contract. Annotation-based tools have such advantages over

comment-based tools in locating and extracting contracts;

14

 Defining contract via annotations is more elegant, and brings the benefit of code

readability and ease of testing;

 Unlike Modern Jass, which doesn’t provide Window version of the Eclipse plug-in jar

file [24], Cofoja is compatible with any system installed with JDK 6 or higher.

15

Chapter 3

DESIGN 3.1 Goals

The goal of the project is to help developers learn and adopt to secure coding practices by providing them a mechanism embedded into the Eclipse code development environment.

The mechanism is supposed to support assistance to users for both defect localization and error correction, either through rule violation checking or contract programming enforcement. The first version of Secure Coding Assistant supports on the fly CERT rule violation detection and provides messages to the developer to educate them on proper coding practices. Therefore, we believe it will be very helpful if the tool is also able to provide suggestions to the developers to fix the vulnerabilities. Besides, “Design by

Contract” methodology will also be supported in the second version by checking the usage of an existing contract programming tool – Cofoja to help the developers locate errors more easily and deliver higher quality software.

3.2 Architecture

To build an early detection tool which provides instant feedback to the developers, the new version of Secure Coding Assistant inherits its previous design to run background checking to monitor code changes and to detect rule violations [6]. For code analysis, the abstract syntax tree (AST) which represents the structure of source code is traversed. When a rule violation is detected, a marker is created at the place where a violation occurs. Any subsequent code changes will clear all the markers created and will trigger a new round of

AST node traversal.

16

In the new version of the Secure Coding Assistant, we find that the detection of the absence of “Design by Contract” can be achieved based on the same design, i.e. AST node traversal afore-mentioned to provide corrective solutions to developers. Based on this observation, the enforcement of contract programming is also done in terms of rule violation detection.

Specifically, rules used for checking the existence of pre-condition, post-condition, and invariant are added to the RuleFactory along with CERT rules (see RuleFactory.java in

Appendix). Upon detection of a rule violation, the newly added method getSolutions() (see

IRule.java and SecureCodingRule.java in Appendix) will be called and the solutions will be rendered to developers along with the problem description. The workflow of the new version of Secure Coding Assistant is shown in (Figure 7) [8]. This figure is the expansion of the previous design for Secure Coding Assistant [6]. The highlighted blocks show the expansion of our new version.

Figure 7. Workflow of Secure Coding Assistant

17

As shown in Figure 7, whenever an event happens, such as the rebuild of project and modification of source code, the reconcile() method of the SecureCompilationParticipant

(see SecureCompilationParticipant.java in Appendix) is triggered and the new AST is compared to the prior one. If the source code has been modified, all of the

InsecureCodeSegments (see InsecureCodeSegment.java in Appendix) generated in the previous round are removed and the insecure coding markers are deleted.

Then, a customized ASTVisitor SecureCodeAnalyzer (see SecureCodeAnalyzer.java) is used to parse the new AST to detect rule violations. It checks each node against all of the rules by calling the violate() function (see IRule.java, DesignByContract.java and

SecureCodingRule.java in Appendix) to find out whether any of the rule has been violated.

Then, it calls the getSolutions() function (see IRule.java, DesignByContract.java and

SecureCodingRule in Appendix) to obtain the instructions to remove the problems and stores them in RULE_SOLUTIONS (see Global.java in Appendix). After that, an object of

InsecureCodeSegment is created to generate the problem marker, and is stored for next round analysis.

The analysis of the AST doesn’t end until the last node has been checked, and the next round of node analysis starts when reconcile() method is called.

18

Chapter 4

IMPLEMENTATION

4.1 Plugin Implementations

Plugin Development Environment (PDE) provided by Eclipse allows developers to extend and customize the development environment [25]. Eclipse was chosen for two reasons: first, it is the most widely used Java IDE that supports various language programming [26].

Second, its extensible Plug-in Development system (PDE) allows developers to develop, test, debug, build and deploy customized plug-ins [27]. In the initial version, two extension points org.eclipse.jdt.core.compilationParticipant and org.eclipse.core.resources.problemmarker (see plugin.xml in Appendix) were extended.

The former one enables the tool to participate in the compilation process when an event, such as build action, clean action or reconcile operation, occurs and initiates code analysis for rule violation detection. The latter one creates a problem marker that is used to indicates the potential security hole in the source code.

In the new version of Secure Coding Assistant, four extension points org.eclipse.ui.ide.markerResolution, org.eclipse.ui.menus, org.eclipse.ui.commands and org.eclipse.ui.handlers are extended.

The first one is used to support “quick fix” feature (Figure 8). This feature provides solutions to the developers, and make them able to fix the problem by one simple click. To provide “quick fix” solutions only to problems with securecodingmarker, the ID of org.eclipse.core.resources.problemmarker is assigned to the markerType of

19 org.eclipse.ui.ide.markerResolution (see plugin.xml in Appendix). In addition, two new attributes, ruleID and hashCode are added to org.eclipse.core.resources.problemmarker to serve as a solution map key. Once a problem marker is created, the solutions to that problem can be retrieved by using the rule ID and hash code as the key.

Figure 8. Quick fixes provided in Secure Coding Assistant

The second and third extension points allow developer create menus and commands to

Eclipse interface. And the org.eclipse.ui.handlers extension point allows the developer define command action. The handler associates a Handler class which overwrites the execute() method to a customized command, so that each time the corresponding button is clicked, the code in the Handler class will be executed (see

ExportContractCommandHandler.java and

EnableContractCheckingCommandHandler.java in Appendix).

20

4.2 AST and ASTWrite

An abstract syntax tree (AST) is used to represent the structure of the source code and each

AST node represents a construct, such as declaration, statement, expression, type, or name

[28]. Since the subtree represented by an AST node can be visited by passing an

ASTVisitor object, developer can traverse and analyze the AST using their customized visitor that inherits the ASTVisitor, such as ASTNodeProcessor, SecureCodeAnalyzer and

SecureCodingNodeVisitor (see ASTNodeProcessor.java, SecureCodeAnalyzer.java and

SecureCodingNodeVisitor.java in Appendix).

Source code rewrite can be achieved by utilizing ASTRewrite infrastructure. This object is used to collect the descriptions of the change made to AST nodes and translate those descriptions into text edits, which are then applied to the source code [29]. In the new version, the getSolutions() method is added to the IRule interface. This method is called whenever a rule is violated. It takes the evaluated AST node as its parameter and returns an instance of TreeMap (see IRule.java in Appendix). The value of the map is an ASTRewrite object, which collects changes to the souce code and is used to generate solution in “quick fix”.

4.3 “Quick Fix” Feature and Source Code Analysis

As mentioned above, the “quick fix” feature is implemented by extending the extension point: org.eclipse.core.resources.problemmarker. To fit into different scenarios, solutions are offered as two groups of options as shown in Figure 9: to fix the issue or to skip rule check. By choosing the former one, the original source code will be modified upon the

21 selected solution (Figure 10) , whereas by choosing the latter one, a @SkipRuleCheck annotation containing the rule name is added before the method (Figure 10) (see

SkipRuleCheck.java in Appendix). As a result, all the violations of the specific rule in the method will be ignored and the corresponding problem marker will be removed. If two or more rules are ignored within the same method, the other rule IDs will be appened after the first one.

Figure 9. Quick fix to random number generation rule violation

Figure 10. Secure random generator quick fix results: use SecureRandom (top) or skip rule check (bottom)

22

To illustrate how Secure Coding Assistant helps the developer fix the problem, an example that fixes “Do not use the Object.equals() method to compare two arrays” rule violation, is used to explain the logic applied in the tool (Figure 11). (see

EXP02J_DoNotUseObjectEqualsToCompareArrays.java in Apendix)

Figure 11. Source code with "Do not use the Object.equals() method to compare two arrays" rule violation

As shown in the figure, a marker is created under the statement of “array1.equals(array2)” because it uses equals() method to compare two arrays. Since the Object.equals() method can only tell the equality of the two objects’ reference but not their contents, using equals() to compare two arrays is not recommended by CERT. The reason for that is because this approach is misleading and developer always uses it to compare the content of the arrays.

The source code of the EXP02J_DoNotUseObjectEqualsToCompareArrays class is shown in Figure 12. The method violated() is used to detect whether this rule has been violated by

23 checking two conditions: first, the evaluated ASTNode is an instance of MethodInvocation.

Second, the caller of the equals() method is an instance of Array. If both conditions are met, the rule is considered to be violated.

Figure 12. The violated() method used to evaluate "Do not use the Object.equals() method to compare two arrays"

Figure 13. The preVisit() method in class SecureNodeAnalyzer

24

Then, as showen in Figure 13, upon the detection of rule violation, the getSolutions() method is called and the solutions are stored in a global map RULE_SOLUTIONS for quick fix generation (see Global.java in Appendix). The returned object has a type of

TreeMap. The key of the map is a String type and is used as the label of the solution in “Quick fix”, and the value of the map is an ASTRewrite object, which collects the descriptions of modification and applied those changes to the source code. In this example, two quick fixes are implemented based on the suggestions provided on CERT website. One solution suggested by CERT is to use the Arrays.equals() method for comparasion. This approach compares the contents of the two arrays, and considers them equivalent if they have the same number of element in same order. The other solution suggested by CERT is to use operator “==” or “!=” for comparison. This approach compares the reference equality of the two arrays, which is an alternative to the equals() method but less misleading. The code used to build “quick fix” solutions is shown in

(Figure 14).

25

Figure 14. The getSolution() method in class EXP02J_DoNotUseObjectEqualstoCompare Arrays

As shown above, to build the first solution, a new MethodInvocation node

Arrays.equals(arr1, arr2) needs to be created to replace the old MethodInvocation arr1.equals(arr2). First, by assigning “Arrays” as the object and setting “equals” as the method, respectively, a MethodInvocation Arrays.equals() is obtained. Then, a copy of the original expression value arr1 and the first argument arr2 are added to the argument list of the new MethodInvocation to provide Arrays.equals(arr1, arr2). After that, the changes are collected by the ASTRewrite object and are stored in TreeMap

26 to generate “quick fix” solutions. Similarly, to implement the second solution which uses operator “==” instead of equals() method for comparision, an InfixExpression node is created. Then a copy of the original expression value arr1 is assigned to the left operand and a copy of the original first argument arr2 is assigned to the right operand to create

MethodInvocation node arr1 == arr2.

Finally, after putting all pairs of labels and the Rewrite objects to the map, the

TreeMap object is stored in Global.RULE_SOLUTIONS which is an instance of HashMap> (see Global.java in

Appendix). The key of that map is the combination of the rule ID and hash value of the node, which are attributes of the secure coding problem marker. These values can be obtained during “quick fix” generation as shown in Figure 15, and can be used to retrieve labels and ASTRewrite object for solution generation. Upon the implementation of the

“quick fix” feature, the available solutions to fix the

EXP02J_DoNotUseObjectEqualsToCompareArrays rule violation are provided as shwn in

(Figure 16).

27

Figure 15. Source code for quick fixes generation

Figure 16. Quick fixes for "Do not use the Object.equals() method to compare two arrays" rule violation

28

4.4 Integration of Design by Contract Methodology

Since there are several contract programming tools that have been developed and being well maintained, it is more reasonable to integrate one of those tools into the Secure Coding

Assistant. In the new version of Secure Coding Assistant, the enforcement of “Design by

Contract” is accomplished by the mandatory usage of Cofoja, which means the tool itself only checks for the presence of the contract annotations against Cofoja’s library, while leaving the syntactic and semantic checking to Cofoja. The same logic used for CERT rule violation detection is applied for checking the presence of the annotations. Three classes,

InvariantCheck, PostConditionCheck and PreConditionCheck, are created by implementing the IRule interface and are added to the RuleFactory (see

InvariantCheck.java, PreConditionCheck.java and PostconditionCheck.java in Appendix).

Durning node analysis, each method or type declaration node is evaluated by calling the violated() method in all the classes to check if the corresponding annotation, such as

@Requires, @Ensures or @Invariant is included. If the contract annotation is found missing in the node, a problem marker will be created. A BookStore class shown in Figure

17 illustrates how this works. In this class, two global variables: count (int) and bookRepository (HashMap) are declared. Since the tool requires the developer provide contract definition to each global variable that is not included in the

@SkipInvariant(), the absence of the invariant definition of count causes the generation of a problem marker (Figure 17). Similarly, for each method, both precondition and postcondition are required to be speficied using @Requires and

@Ensures/@ThrowEnsures annotations if the condition is not included in

29

@SkipConditionCheck(). For example, as shown in (Figure 18), the developer doesn’t specify precondition using @Requires in the BookStore constractor. Theoretically, a problem marker will be generated, however, because the developer uses

@SkipConditionCheck to indicate that the precondition will not be provided, no error marker is shown. Whereas, in the findBook() method, because neither the definition of precondition nor the @SkipConditionCheck() is provided, the precondition rule is considered to be violated and a problem marker is generated.

Figure 17. Class global variable missing restriction

30

Figure 18. Method skip pre-condition check (top) and method missing pre-condition check (bottom)

4.5 “Export Contract Annotation” Feature

To facilitate documentation, the “Export Contract Annotation” feature is added to Secure

Coding Assistant. As mentioned in section 4.1, the implementation of “Secure Coding

Assistant” menu in Eclipse interface and “Export Contract Annotation” command are achieved by extending the following extension points: org.eclipse.ui.menus, org.eclipse.ui.commands and org.eclipse.ui.handlers. In the

ExportContractCommandHandler class, that inherits AbstractHandler, the execute() method has been overwritten (see ExportContractCommandHandler.java in Appendix), so that whenever the “Export Contract Annotation” button (Figure 19) is clicked, the contract annotation along with the method signature are exported to a text file in the folder of “securecodingassistant_output” as shown in Figure 20.

31

Figure 19. Customized menu

Figure 20. Export contract annotation of BookStore class to text file

4.6 “Disable/Enable Design by Contract Enforcement” Feature

Similer to the “Export Annotatin” feature, “Disable/Enable Design by Contract

Enforcement” command is implemented by overwriting the execute() method in

EnableContractCheckingCommandHandler class (see

EnableContractCheckingCommandHandler.java in Appendix). The reason to add this feature to our tool is because the developer needs to remove the contract restriction before software delivery, and with “Enforcement of Design by Contract” feature enabled, the developer will keep getting alerts of contract rule violation. Therefore, a command that allows the developers make their own choice upon different scenario is implemented. As

32 shown in Figure 19, by checking the “Disable Design by Contract Enforcement” option, the contract checking rules: “InvariantCheck.java”, “PostConditionCheck.java” and

“PreconditionCheck.java” are removed from the RuleFactory and the contract conditions will not be checked during node analysis (see SecureCompilationParticipant.java and

RuleFactory.java in Appendix). As shown in Figure 21, after Design by Contract enforcement feature is disabled, no problem marker is generated even the precondition contract is not provided.

Figure 21. Disable "Design by Contract Enforcement"

33

Chapter 5

LIMITATION, CONCLUSION AND FUTURE WORK

This report presents a coding assistance tool, which supports the detection of CERT rule violations, the enforcement of contract programming, and a one-click feature to help users quickly correct the detected code defects. As an open source development, the tool can serve as a practical and efficient application in educating developers on robust programming practices.

However, for proof of concept, current implementation priorities are given to only sample rules in a small subset (about one quarter) of CERT library - only 21 CERT rule violations are currently defined. In addition, the capability of the quick fix feature has not been evaluated for all cases.

Therefore, the future work will focus on expansion of the rule set, thorough tests of the tool features, as well as education evaluations with the tool applied in possible computer science classes with Java programming.

34

Appendix

Source code (all the code including the former student’s work is attached in order to increase the readability. The author(s) of the code is(are) provided in the comment before each class/method)

Plugin.xml ( the use of the file is introducted in Section 4.1, 4.3)

This file provides definition of all the extension of the “Secure coding assistant” tool. The first two extensions, “SecureCompilationParticipants” and

“securecodingmarker” are provided by the Ben White, et al. While the others have been implemented in this project.

35

36

37

SkipConditionCheck.java, SkipInvariantCheck.java and SkipRuleCheck.java (the use of the file is introducted in Section 4.3, 4.4, 4.6)

Skip rule annotation and skip contract annotation are implemented to provide an option to skip secure coding rule and contract checking. By adding the skipped rule id or contract name as the values of the corresponding annotation, the check of the rule violation is waived.

/** * SkipConditionCheck annotation indicates the absence of contract definition. * @author Chen Li */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.CONSTRUCTOR }) public @interface SkipConditionCheck { String[] value(); }

/** * SkipInvariantCheck annotation indicates the absence of definition. * @author Chen Li */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SkipInvariantCheck { String[] value(); }

/** * SkipRuleCheck annotation indicates the specific CERT rule violation will be ignored. * @author Chen Li */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.CONSTRUCTOR }) public @interface SkipRuleCheck { String[] value(); }

38

IRule.java ( the use of the file is introducted in Section 3.2, 4.1- 4.4)

IRule is an interface which will be implemented by all of the secure coding rules and contract rules. This interface defines a method violate() to check the violation of the corresponding rule and several methods to provide the information of the violated rule, including the name, the ID, the description and the recommend solution of the rule.

/** * Interface for a secure coding rule * @author Ben White, Chen Li */ public interface IRule {

/** * Checks to see if the rule has been violated in a given node * @param node The node to be evaluated * @return true if the rule was violated, false otherwise * @author Ben White */ public boolean violated(ASTNode node);

/** * The description of the rule that was violated * @return The description of the rule that was violated * @author Ben White */ public String getRuleText();

/** * The name of the rule violated * @return The name of the rule violated * @author Ben White */ public String getRuleName();

/** * The ID of the rule violated * @return The name of the rule violated * @author Ben White */ public String getRuleID();

/** * The recommended action that will satisfy the rule * @return The recommended action that will satisfy the rule * @author Ben White */ public String getRuleRecommendation();

/** * The security level of the violated rule} * @return A {@link Globals.Markers} security level * @see Globals.Markers * @author Ben White */ public int securityLevel();

39

/** * The recommended solution that will fix the violation * @return The proposed solution * @author Chen Li */ public TreeMap getSolutions(ASTNode node);

/** * The ICompilationUnit needed to edit the source code * @return The ICompilationUnit * @author Chen Li */ public ICompilationUnit getICompilationUnit();

40

RuleFactory.java (This file is introduced in Section 3.2, 4.6)

This class contains all the rules used in Secure Coding Assistant and the containers to store solutions in “quick fix”.

/** * Creates a set of rules that implement the IRule interface * @author Ben White, Chen Li * @see IRule */ public final class RuleFactory { /** * Cannot instantiate * @author Ben White */ private RuleFactory() {

}

/** * Creates a collection of all possible secure coding rules to test * @return A collection of all possible secure coding rules to test * @author Ben White */ public static ArrayList getAllCERTRules() { ArrayList rules = new ArrayList();

// 00. Input Validation and Data Sanitization rules.add(new IDS00J_PreventSQLInjection()); rules.add(new IDS01J_NormalizeStringsBeforeValidating()); rules.add(new IDS07J_RuntimeExecMethod()); rules.add(new IDS11J_ModifyStringsBeforeValidation());

// 01. Declarations and Initialization rules.add(new DCL02J_DoNotModifyElements());

// 02. Expressions rules.add(new EXP00J_DoNotIgnoreValuesReturnedByMethods()); rules.add(new EXP02J_DoNotUseObjectEqualsToCompareArrays());

// 03. Numeric Types and Operations rules.add(new NUM07J_DoNotAttemptComparisonsWithNaN()); rules.add(new NUM09J_DoNotUseFloatingPointAsLoopCounters());

// 04. Characters and Strings rules.add(new STR00J_PartialCharFromVarWidthEnc());

// 05. Object Orientation rules.add(new OBJ09J_CompareClassesAndNotClassNames());

// 06. Methods rules.add(new MET04J_DoNotIncreaseTheAccessibilityOfOveriddenMethods()); rules.add(new MET06J_DoNotInvokeOverridableMethodsInClone());

// 07. Exceptional Behavior rules.add(new ERR08J_DoNotCatchNullPointerException());

// 09. Locking rules.add(new LCK09J_DoNotPerformOperationsThatCanBlockWhileHoldingLock());

41

// 10. Thread APIs rules.add(new THI05J_DoNotUseThreadStopToTerminateThreads());

// 13. Input Output rules.add(new FIO08J_DistinguishBetweenCharactersOrBytes());

// 14. Serialization rules.add(new SER01J_DoNotDeviateFromTheProperSignaturesOfSerializationMethods());

// 15. Platform Security rules.add(new SEC07J_CallTheSuperclassGetPermissionsMethod());

// 16. Runtime Environment rules.add(new ENV02J_DoNotTurstTheValuesOfEnvironmentVariables());

// 49. Miscellaneous rules.add(new MSC02J_GenerateStrongRandomNumbers());

return rules; }

/** * Creates a collection of all contract rules to test * @return A collection of all contract rules to test * @author Chen Li */ public static ArrayList getAllContractRules() { ArrayList rules = new ArrayList();

// PreCondition, PostCondition, Invariant check rules.add(new PostConditionCheck()); rules.add(new PreConditionCheck()); rules.add(new InvariantCheck());

return rules; } }

42

SecureCodingRule.java and DesignByContractRule.java ( the use of the files are introducted in Section 3.2, 4.3 and 4.4)

This two abstract class implement IRule interface. The violate() method checks whether the violated rule has been waived or the definition of the pre/postcondition has been skipped respectively. The getSolutions() method provide instruction to skip the secure coding rule check or the definition of the missing contract repectively.

/** * Check the presence of skip rule annotations and build * solution to remove the problem due to the violation of * CERT rule * @author Chen Li */ public abstract class SecureCodingRule implements IRule{ /** * Check the presence of skip secure coding rule annotations for the violated * rule */ @Override public boolean violated(ASTNode node) { //Check whether the rule check is skipped ASTNode parent = node; while (parent != null && !(parent instanceof MethodDeclaration)) { parent = parent.getParent(); }

if (parent != null) {

MethodDeclaration methodDeclaration = (MethodDeclaration) parent;

// find whether method contains @SkipRuleCheck annotation List modifiers = methodDeclaration.modifiers(); SingleMemberAnnotation skipRuleAnnotation = null;

for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; Name typeName = annotation.getTypeName(); if (Globals.Annotations.SKIP_RULE_CHECK.equals(typeName.getFullyQualifiedName())) { skipRuleAnnotation = annotation; break; } } }

if (skipRuleAnnotation != null) { // method contains @SkipRuleCheck annotation ArrayInitializer newValue = (ArrayInitializer) skipRuleAnnotation.getValue(); for (Object obj : newValue.expressions()) { if (obj instanceof StringLiteral) {

43

StringLiteral sl = (StringLiteral) obj; if (sl.getLiteralValue().equals(getRuleID())) return false; } } } } return true; }

/** * Provide solution to skip the check of the violated secure coding rule */ @SuppressWarnings("unchecked") @Override public TreeMap getSolutions(ASTNode node) { if (!violated(node)) throw new IllegalArgumentException("This node doesn't violate rule, so no suggest solution");

TreeMap solutionMap = new TreeMap<>(); // Add skip rule check to solutionList ASTNode parent = node; while (parent != null && !(parent instanceof MethodDeclaration)) { parent = parent.getParent(); }

if (parent != null) {

MethodDeclaration methodDeclaration = (MethodDeclaration) parent; AST methodAST = methodDeclaration.getAST(); ASTRewrite rewrite = ASTRewrite.create(methodAST);

// find whether method contains @SkipRuleCheck annotation List modifiers = methodDeclaration.modifiers(); SingleMemberAnnotation skipRuleAnnotation = null;

for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; Name typeName = annotation.getTypeName(); if (Globals.Annotations.SKIP_RULE_CHECK.equals(typeName.getFullyQualifiedName())) { skipRuleAnnotation = annotation; } } }

StringLiteral sl = methodAST.newStringLiteral(); sl.setLiteralValue(getRuleID());

if (skipRuleAnnotation != null) { // method contains @SkipRuleCheck annotation Expression oldExpression = skipRuleAnnotation.getValue(); ArrayInitializer array = (ArrayInitializer) oldExpression; ListRewrite listRewrite = rewrite.getListRewrite(array, ArrayInitializer.EXPRESSIONS_PROPERTY); listRewrite.insertLast(sl, null);

} else { // method doesn't contain @SkipRuleCheck annotation SingleMemberAnnotation newAnnotation = methodAST.newSingleMemberAnnotation();

44

newAnnotation.setTypeName(methodAST.newName(Globals.Annotations.SKIP_RULE_CHECK)); ArrayInitializer newValue = methodAST.newArrayInitializer(); newValue.expressions().add(sl); newAnnotation.setValue(newValue); rewrite.getListRewrite(methodDeclaration, methodDeclaration.getModifiersProperty()) .insertFirst(newAnnotation, null); } solutionMap.put("Skip Rule Check", rewrite);

} return solutionMap; }

@Override public ICompilationUnit getICompilationUnit(){ return null; }

}

/** * Check the presence of skip condition annotations for the violated rule and build * solution to remove the problem due to the absence of contract definition * @author Chen Li */ public abstract class DesignByContractRule implements IRule {

/** * Check the presence of skip contract annotations for the violated * contract rule */ @Override public boolean violated(ASTNode node) { //Check whether the condition check is skipped

if (node instanceof MethodDeclaration) {

MethodDeclaration methodDeclaration = (MethodDeclaration) node;

// find whether method contains @SkipConditionCheck annotation List modifiers = methodDeclaration.modifiers(); SingleMemberAnnotation skipConditionAnnotation = null;

for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; Name typeName = annotation.getTypeName(); if (Globals.Annotations.SKIP_CONDITION_CHECK.equals(typeName.getFullyQualifiedName())) { skipConditionAnnotation = annotation; break; } } }

if (skipConditionAnnotation != null) { // method contains @SkipConditionCheck annotation ArrayInitializer newValue = (ArrayInitializer) skipConditionAnnotation.getValue(); for (Object obj : newValue.expressions()) {

45

if (obj instanceof StringLiteral) { StringLiteral sl = (StringLiteral) obj; if (sl.getLiteralValue().equals(getRuleID())) return false; } } } } return true; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_HIGH; }

public boolean isPublic(MethodDeclaration node) { List modifiers = node.modifiers(); for (Object obj: modifiers) { if (obj instanceof Modifier) { Modifier m = (Modifier) obj; if (m.isPublic()) return true; } } return false; }

public boolean isPublic(TypeDeclaration node) { List modifiers = node.modifiers(); for (Object obj: modifiers) { if (obj instanceof Modifier) { Modifier m = (Modifier) obj; if (m.isPublic()) return true; } } return false; }

/** * Provide solution to skip the check of the violated contract rule */ @SuppressWarnings("unchecked") @Override public TreeMap getSolutions(ASTNode node) {

TreeMap solutionMap = new TreeMap<>(); if (node instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration)node; AST methodAST = methodDeclaration.getAST(); ASTRewrite rewrite = ASTRewrite.create(methodAST);

// find whether method contains @SkipConditionCheck annotation List modifiers = methodDeclaration.modifiers(); SingleMemberAnnotation skipConditionAnnotation = null;

for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; Name typeName = annotation.getTypeName(); if (Globals.Annotations.SKIP_CONDITION_CHECK.equals(typeName.getFullyQualifiedName())) {

46

skipConditionAnnotation = annotation; } } }

StringLiteral sl = methodAST.newStringLiteral(); sl.setLiteralValue(getRuleID()); ArrayInitializer newArrayInitializer = methodAST.newArrayInitializer();

if (skipConditionAnnotation != null) { // method contains @SkipConditionCheck annotation, add ruleID to value Expression oldExpression = skipConditionAnnotation.getValue(); ArrayInitializer array = (ArrayInitializer) oldExpression; ListRewrite listRewrite = rewrite.getListRewrite(array, ArrayInitializer.EXPRESSIONS_PROPERTY); listRewrite.insertLast(sl, null);

} else { // method doesn't contain @SkipConditionCheck annotation, insert annotation SingleMemberAnnotation newAnnotation = methodAST.newSingleMemberAnnotation();

newAnnotation.setTypeName(methodAST.newName(Globals.Annotations.S KIP_CONDITION_CHECK)); newArrayInitializer.expressions().add(sl); newAnnotation.setValue(newArrayInitializer); rewrite.getListRewrite(methodDeclaration, methodDeclaration.getModifiersProperty()) .insertFirst(newAnnotation, null); } solutionMap.put("Skip Condition Check", rewrite); } return solutionMap; }

@Override public ICompilationUnit getICompilationUnit(){ return null; }

}

47

All of the classes used to define the rules (3 contract rules and 21 secure coding rule) ( the use of the files are introducted in Section 4.3 and 4.4)

The PreconditionCheck, PostconditionCheck and InvariantCheck implement

DesignByContract class. The violate() method check whether the developer defined the precondition, poscondition and invariant respectively. The getSolutions() method provides instruction to generate the corresponding missing annotations: @Requires,

@Ensures/@ThrowEnsures and @Invariant.

/** * Check the presence of precondition annotations and build * solution to remove the problem due to the absence of contract definition * @author Chen Li */ public class PreConditionCheck extends DesignByContractRule{

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

// Is node a method declaration? if (node instanceof MethodDeclaration) {

MethodDeclaration methodDeclaration = (MethodDeclaration) node;

// find whether method contains @Requires List modifiers = methodDeclaration.modifiers(); if (isPublic(methodDeclaration) && !(methodDeclaration.getName().getIdentifier().equals("main"))) { ruleViolated = true; for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; String name = annotation.getTypeName().getFullyQualifiedName(); if (name.equals(Globals.Annotations.REQUIRES)) { ruleViolated = false; break; } } } }

if (ruleViolated) ruleViolated = super.violated(node); } return ruleViolated; }

@Override public String getRuleText() {

48

return "Public method should have preconditoin check"; }

@Override public String getRuleName() { return Globals.RuleNames.PRECONDITION_CHECK; }

@Override public String getRuleRecommendation() { final String res = String.format("Add precondition check @%s({\"\"}) or skip precondition check @%s", Globals.Annotations.REQUIRES, Globals.Annotations.SKIP_CONDITION_CHECK); return res; }

/** * Provide instruction to add requires annotation before the method */ @Override public TreeMap getSolutions(ASTNode node) { if (!(node instanceof MethodDeclaration)) throw new IllegalArgumentException("Expected MethodDeclaration, but " + node.getAST());

AST ast = node.getAST(); MethodDeclaration md = (MethodDeclaration)node;

//Solution 1: add @Requires check SingleMemberAnnotation annotation1 = ast.newSingleMemberAnnotation(); annotation1.setTypeName(ast.newName(Globals.Annotations.REQUIRES)); annotation1.setValue(ast.newArrayInitializer()); ASTRewrite rewrite1 = ASTRewrite.create(ast); rewrite1.getListRewrite(md, md.getModifiersProperty()).insertFirst(annotation1, null);

// Add solutions to list; TreeMap list = new TreeMap(); list.putAll(super.getSolutions(node)); list.put("Add @Requires annotation", rewrite1); return list; }

@Override public String getRuleID() { return Globals.RuleID.PRECONDITION; }

}

/** * Check the presence of post condition annotations and build * solution to remove the problem due to the absence of contract definition * @author Chen Li */ public class PostConditionCheck extends DesignByContractRule{ /** * Check the presence of ensure annotation or throw ensure annotation */ @Override public boolean violated(ASTNode node) {

boolean ruleViolated = false;

// Is node a method declaration? if (node instanceof MethodDeclaration) {

49

MethodDeclaration methodDeclaration = (MethodDeclaration) node;

// find whether method contains @Ensures, @ThrowEnsure List modifiers = methodDeclaration.modifiers();

if (isPublic(methodDeclaration) && !(methodDeclaration.getName().getIdentifier().equals("main"))) { ruleViolated = true; for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; String name = annotation.getTypeName().getFullyQualifiedName(); if (name.equals(Globals.Annotations.ENSURES) || name.equals(Globals.Annotations.THROW_ENSURES)) { ruleViolated = false; break; } } } }

if (ruleViolated) ruleViolated = super.violated(node); } return ruleViolated;

}

@Override public String getRuleText() { return "Public method should have postconditoin check"; }

@Override public String getRuleName() { return Globals.RuleNames.POSTCONDITION_CHECK; }

@Override public String getRuleRecommendation() { final String res = String.format( "Add postcondition check @%s({\"\"}) or throw exception@%s({\"\"}) or skip postcondition check @%s", Globals.Annotations.ENSURES, Globals.Annotations.THROW_ENSURES, Globals.Annotations.SKIP_CONDITION_CHECK); return res; }

/** * Provide instruction to add ensures/throw ensures annotation before the method */ @Override public TreeMap getSolutions(ASTNode node) { if (!(node instanceof MethodDeclaration)) throw new IllegalArgumentException("Expected MethodDeclaration, but " + node.getAST());

AST ast = node.getAST(); MethodDeclaration md = (MethodDeclaration)node;

//Solution 1: add @Ensures check

50

SingleMemberAnnotation annotation1 = ast.newSingleMemberAnnotation(); annotation1.setTypeName(ast.newName(Globals.Annotations.ENSURES)); annotation1.setValue(ast.newArrayInitializer()); ASTRewrite rewrite1 = ASTRewrite.create(ast); rewrite1.getListRewrite(md, md.getModifiersProperty()).insertFirst(annotation1, null);

//Solution 2: add @ThrowEnsures SingleMemberAnnotation annotation2 = ast.newSingleMemberAnnotation(); annotation2.setTypeName(ast.newName(Globals.Annotations.THROW_ENSURES)); annotation2.setValue(ast.newArrayInitializer()); ASTRewrite rewrite2 = ASTRewrite.create(ast); rewrite2.getListRewrite(md, md.getModifiersProperty()).insertFirst(annotation2, null);

// Add solutions to list; TreeMap map = new TreeMap (); map.putAll(super.getSolutions(node)); map.put("Add @Ensures check", rewrite1); map.put("Add @ThrowEnsures exception", rewrite2); return map; }

@Override public String getRuleID() { return Globals.RuleID.POSTCONDITION; }

}

/** * Check the presence of invariant annotations and build * solution to remove the problem due to the absence of invariant definition * @author Chen Li */ public class InvariantCheck extends DesignByContractRule{ /** * Check the presence of skip contract annotations and compare * the variables defined in the contract with declared variable to find * out if any of the variables does not have a invariant check */ @Override public boolean violated(ASTNode node) {

// Is node a type declaration? if (!(node instanceof TypeDeclaration)) return false;

TypeDeclaration typeDeclaration = (TypeDeclaration) node; // if not public class, invariantCheck is not mandatory if (!isPublic(typeDeclaration)) return false;

// get all variable name and all variable store in Invariant and SkipInvariant Check HashSet variableNames = getAllVariables(typeDeclaration); HashSet variableInInvariantCheck = getAllVariableInInvariant(typeDeclaration); HashSet variableInSkipInvariantCheck = getAllVariableInSkipInvariant(typeDeclaration);

// return true if no variable and no invariant check, or has // variable and match with invariant/skip invariant check

51

if (variableNames.isEmpty() && variableInInvariantCheck.isEmpty() && variableInSkipInvariantCheck.isEmpty()) { return false; } else if (!variableNames.isEmpty() && (!variableInInvariantCheck.isEmpty() || !variableInSkipInvariantCheck.isEmpty())) { // number of variable doesn't match with the sum of the number of // variable in Invariant/SkipInvariant Check if (variableNames.size() != (variableInInvariantCheck.size() + variableInSkipInvariantCheck.size())) { return true; } else { for (String s : variableNames) { if (!variableInInvariantCheck.contains(s) && !variableInSkipInvariantCheck.contains(s)){ return true; } } return false; } } else return true; }

@Override public String getRuleText() { return "Variables should have invariant check"; }

@Override public String getRuleName() { return Globals.RuleNames.INVARIANT_CHECK; }

@Override public String getRuleRecommendation() { final String res = String.format("Add invariant check @%s({\"\"}) or skip invariant check @%s", Globals.Annotations.INVARIANT, Globals.Annotations.SKIP_INVARIANT_CHECK); return res; }

/** * Provide instructions to remove the violation. First option is to provide the * definition of contract using invariant annotation. Second option is to * waive the contract definition by using skip invariant annotation. */ @SuppressWarnings("unchecked") @Override public TreeMap getSolutions(ASTNode node) { if (!(node instanceof TypeDeclaration)) throw new IllegalArgumentException("Expected TypeDeclaration, but " + node.getAST());

// Solution list; TreeMap map = new TreeMap();

TypeDeclaration typeDeclaration = (TypeDeclaration) node; AST ast = node.getAST(); // get all variable name and all variable store in Invariant and SkipInvariant Check HashSet variableNames = getAllVariables(typeDeclaration); HashSet variableInInvariantCheck = getAllVariableInInvariant(typeDeclaration); HashSet variableInSkipInvariantCheck = getAllVariableInSkipInvariant(typeDeclaration);

52

//find the nodes of @Invariant and @SkipInvariant SingleMemberAnnotation invariant = null; SingleMemberAnnotation skipInvariant = null; List modifiers = typeDeclaration.modifiers(); for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; String name = annotation.getTypeName().getFullyQualifiedName(); if (name.equals(Globals.Annotations.INVARIANT)) { invariant = annotation; } else if (name.equals(Globals.Annotations.SKIP_INVARIANT_CHECK)) { skipInvariant = annotation; } } }

//Case 1: no declared variable but has variable in @Invariant or @SkipInvariant, delete the annotation if (variableNames.isEmpty() && (invariant!= null || skipInvariant != null)) { ASTRewrite rewrite1 = ASTRewrite.create(ast); if (invariant != null) { rewrite1.remove(invariant, null); } if (skipInvariant != null) rewrite1.remove(skipInvariant, null); map.put("Element in @Invariant or @SkipInvariant is not defined, remove unused annotation", rewrite1); }

//Case 2: some variable in @Invariant or @skipInvariant is not defined, delete those variable HashSet undefinedVariableExistInSkipInvariantCheck = new HashSet<>(variableInSkipInvariantCheck); HashSet undefinedVariableExistInInvariantCheck = new HashSet<>(variableInInvariantCheck); undefinedVariableExistInSkipInvariantCheck.removeAll(variableNames); undefinedVariableExistInInvariantCheck.removeAll(variableNames); if (!undefinedVariableExistInSkipInvariantCheck.isEmpty() || !undefinedVariableExistInInvariantCheck.isEmpty()) { ASTRewrite rewrite2 = ASTRewrite.create(ast); if (!undefinedVariableExistInInvariantCheck.isEmpty()) { ArrayInitializer array = (ArrayInitializer)invariant.getValue(); for (Object obj: array.expressions()) { if (obj instanceof StringLiteral) { StringLiteral sl = (StringLiteral) obj; String slValue = sl.getLiteralValue(); StringTokenizer st = new StringTokenizer(slValue, " .<>=!("); if (undefinedVariableExistInInvariantCheck.contains(st.nextToken())) { rewrite2.remove(sl, null); } } } } if (!undefinedVariableExistInSkipInvariantCheck.isEmpty()) { ArrayInitializer array = (ArrayInitializer)skipInvariant.getValue(); for (Object obj: array.expressions()) { if (obj instanceof StringLiteral) { StringLiteral sl = (StringLiteral) obj;

53

if (undefinedVariableExistInSkipInvariantCheck.contains(sl.getLiteralValue())) { rewrite2.remove(sl, null); } } } } map.put("Remove undefied variable from annotation", rewrite2); }

//Case 3: some variable is not included in either @Invariant or @SkipInvariant HashSet unCheckedVariable = new HashSet<>(variableNames); unCheckedVariable.removeAll(variableInInvariantCheck); unCheckedVariable.removeAll(variableInSkipInvariantCheck); if (!unCheckedVariable.isEmpty()) { // Add those variable to @Invariant ASTRewrite rewrite3 = ASTRewrite.create(ast); if (invariant == null) { invariant = ast.newSingleMemberAnnotation();

invariant.setTypeName(ast.newName(Globals.Annotations.INVARIANT)); ArrayInitializer array = ast.newArrayInitializer(); for (String s : unCheckedVariable) { StringLiteral sl = ast.newStringLiteral(); sl.setLiteralValue(s); array.expressions().add(sl); } invariant.setValue(array); rewrite3.getListRewrite(typeDeclaration, typeDeclaration.getModifiersProperty()).insertFirst(invariant, null); } else { ListRewrite listRewrite3 = rewrite3.getListRewrite((ArrayInitializer) invariant.getValue(), ArrayInitializer.EXPRESSIONS_PROPERTY); for (String s : unCheckedVariable) { StringLiteral sl = ast.newStringLiteral(); sl.setLiteralValue(s); listRewrite3.insertLast(sl, null); } } map.put("Add unchecked variables to @Invariant", rewrite3);

// Add those variable to @SkipInvariant ASTRewrite rewrite4 = ASTRewrite.create(ast); if (skipInvariant == null) { skipInvariant = ast.newSingleMemberAnnotation();

skipInvariant.setTypeName(ast.newName(Globals.Annotations.SKIP_INVARIANT_CHECK)); ArrayInitializer array = ast.newArrayInitializer(); for (String s : unCheckedVariable) { StringLiteral sl = ast.newStringLiteral(); sl.setLiteralValue(s); array.expressions().add(sl); } skipInvariant.setValue(array); rewrite4.getListRewrite(typeDeclaration, typeDeclaration.getModifiersProperty()) .insertFirst(skipInvariant, null); } else { ListRewrite listRewrite4 = rewrite4.getListRewrite((ArrayInitializer) skipInvariant.getValue(), ArrayInitializer.EXPRESSIONS_PROPERTY); for (String s : unCheckedVariable) { StringLiteral sl = ast.newStringLiteral();

54

sl.setLiteralValue(s); listRewrite4.insertLast(sl, null); } } map.put("Add unchecked variables to @SkipInvariant", rewrite4);

}

return map; }

@Override public String getRuleID() { return Globals.RuleID.INVARIANT; }

private HashSet getAllVariables(TypeDeclaration typeDeclaration){ HashSet variableNames = new HashSet<>();

// get all FieldDeclaration FieldDeclaration[] fieldDeclarations = typeDeclaration.getFields();

// get all variable name for (FieldDeclaration fd : fieldDeclarations) { for (Object obj : fd.fragments()) { if (obj instanceof VariableDeclarationFragment) { VariableDeclarationFragment vdf = (VariableDeclarationFragment) obj; variableNames.add(vdf.getName().getFullyQualifiedName()); } } } return variableNames; }

private HashSet getAllVariableInInvariant(TypeDeclaration typeDeclaration){ HashSet variableInInvariantCheck = new HashSet<>(); List modifiers = typeDeclaration.modifiers(); for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; String name = annotation.getTypeName().getFullyQualifiedName(); if (name.equals(Globals.Annotations.INVARIANT)) { ArrayInitializer array = (ArrayInitializer) annotation.getValue(); for (Object obj : array.expressions()) { if (obj instanceof StringLiteral) { StringLiteral sl = (StringLiteral) obj; String value = sl.getLiteralValue(); StringTokenizer defaultTokenizer = new StringTokenizer(value, ".( =>

variableInInvariantCheck.add(variableName); } } } } } return variableInInvariantCheck; }

55

private HashSet getAllVariableInSkipInvariant(TypeDeclaration typeDeclaration) { HashSet variableInSkipInvariantCheck = new HashSet<>(); List modifiers = typeDeclaration.modifiers(); for (Object object : modifiers) { if (object instanceof SingleMemberAnnotation) { SingleMemberAnnotation annotation = (SingleMemberAnnotation) object; String name = annotation.getTypeName().getFullyQualifiedName(); if (name.equals(Globals.Annotations.SKIP_INVARIANT_CHECK)) { ArrayInitializer array = (ArrayInitializer) annotation.getValue(); for (Object obj : array.expressions()) { if (obj instanceof StringLiteral) { StringLiteral sl = (StringLiteral) obj;

variableInSkipInvariantCheck.add(sl.getLiteralValue()); } } } } } return variableInSkipInvariantCheck; }

}

56

The 21 CERT rule classes implement SecureCodingRule class. The violate() method checks whether the corresponding CERT rule is violated. The getSolutions() method provides instruction to generate the solutions to remove the problem. The details of the rule information is provided in the comment of source code.

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: DCL02-J. Do not modify the collection's elements during an * enhanced for statement *

*

* CERT Website: Unlike the basic for statement, assignments to the loop variable fail to affect the * loop's iteration order over the underlying set of objects. Consequently, an assignment * to the loop variable is equivalent to modifying a variable local to the loop body whose * initial value is the object referenced by the loop iterator. This modification is not * necessarily erroneous but can obscure the loop functionality or indicate a * misunderstanding of the underlying implementation of the enhanced for statement. *

*

* Declare all enhanced for statement loop variables final. The final declaration causes * Java compilers to flag and reject any assignments made to the loop variable. *

* @author Ben White (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: DCL02-J */ class DCL02J_DoNotModifyElements extends SecureCodingRule {

@Override public boolean violated(ASTNode node) {

boolean ruleViolated = false;

if(node instanceof EnhancedForStatement) {

EnhancedForStatement statement = (EnhancedForStatement)node;

ruleViolated = true; SingleVariableDeclaration dec = statement.getParameter(); // Look to see if they declared as final for (Object o : dec.modifiers()) { assert o instanceof Modifier; Modifier m = (Modifier) o; if (m.isFinal()) { ruleViolated = false; break; } }

//if node violates rule, check whether method contains skip rule check if (ruleViolated) { ruleViolated = super.violated(node);

57

} }

return ruleViolated; }

@Override public String getRuleText() { return "CERT Website-Unlike the basic for statement, assignments to the loop " + "variable fail to affect the loop's iteration order over the underlying " + "set of objects. Consequently, an assignment to the loop variable is " + "equivalent to modifying a variable local to the loop body whose initial" + " value is the object referenced by the loop iterator. This modification" + " is not necessarily erroneous but can obscure the loop functionality or" + " indicate a misunderstanding of the underlying implementation of the " + "enhanced for statement."; }

@Override public String getRuleName() { return Globals.RuleNames.DCL02_J; }

@Override public String getRuleRecommendation() { return "Declare all enhanced for statement loop variables final. The final " + "declaration causes Java compilers to flag and reject any assignments" + " made to the loop variable."; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_LOW; }

@Override public TreeMap getSolutions(ASTNode node) {

TreeMap map = new TreeMap(); map.putAll(super.getSolutions(node)); try { AST ast = node.getAST(); EnhancedForStatement statement = (EnhancedForStatement) node;

// Solution 1 add "final" before variable ASTRewrite rewrite1 = ASTRewrite.create(ast); SingleVariableDeclaration oldSingleVariableDeclaration = statement.getParameter(); ListRewrite listRewrite = rewrite1.getListRewrite(oldSingleVariableDeclaration, SingleVariableDeclaration.MODIFIERS2_PROPERTY); listRewrite.insertFirst(ast.newModifier(ModifierKeyword.FINAL_KEYWORD), null);

map.put("Add final before variable", rewrite1); } catch (Exception e) { throw new IllegalArgumentException(e); }

58

return map; }

@Override public String getRuleID() { return Globals.RuleID.DCL02_J; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: ENV02-J. Do not trust the values of environment variables *

*

* CERT Website: Programs that execute in a more trusted domain than their environment must * assume that the values of environment variables are untrusted and must sanitize and * validate any environment variable values before use. *

*

* The default values of system properties are set by the Java Virtual Machine (JVM) upon * startup and can be considered trusted. However, they may be overridden by properties * from untrusted sources, such as a configuration file. System properties from untrusted * sources must be sanitized and validated before use. *

*

* Actually, relying on environment variables is more than a portability issue. An attacker * can essentially control all environment variables that enter a program using a mechanism * such as the java.lang.ProcessBuilder class. *

*

* Consequently, when an environment variable contains information that is available by * other means, including system properties, that environment variable must not be used. * Finally, environment variables must not be used without appropriate validation. *

* @author Ben White (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: ENV02-J */ class ENV02J_DoNotTurstTheValuesOfEnvironmentVariables extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

// Is node a method invocation? if(node instanceof MethodInvocation) { // Was System.getenv() called? ruleViolated = Utility.calledMethod((MethodInvocation) node, System.class.getCanonicalName(), "getenv");

//if node violates rule, check whether method contains skip rule check if (ruleViolated) { ruleViolated = super.violated(node); } }

return ruleViolated; }

@Override public String getRuleText() {

59

return "CERT Website-Programs that execute in a more trusted domain than their " + "environment must assume that the values of environment variables are " + "untrusted and must sanitize and validate any environment variable values " + "before use."; }

@Override public String getRuleName() { return Globals.RuleNames.ENV02_J; }

@Override public String getRuleRecommendation() { return "Avoid calls to the System.getenv() command"; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_MEDIUM; }

@SuppressWarnings("unchecked") @Override public TreeMap getSolutions(ASTNode node) {

TreeMap map = new TreeMap<>(); map.putAll(super.getSolutions(node));

try {

AST ast = node.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); MethodInvocation oldMethodInvocation = (MethodInvocation) node; MethodInvocation newMethodInvocation = ast.newMethodInvocation(); SimpleName name = ast.newSimpleName("System"); newMethodInvocation.setExpression(name); newMethodInvocation.setName(ast.newSimpleName("getProperty")); StringLiteral sl = ast.newStringLiteral(); sl.setLiteralValue("user.name"); newMethodInvocation.arguments().add(sl);

rewrite.replace(oldMethodInvocation, newMethodInvocation, null);

map.put("Change to System.getProperty(\"user.name\")", rewrite);

} catch (Exception e) { throw new IllegalArgumentException(e); } return map; }

@Override public String getRuleID() { return Globals.RuleID.ENV02_J; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: ERR08-J. Do not catch NullPointerException or any of its * ancestors

60

*

*

* CERT Webiste: Programs must not catch java.lang.NullPointerException. * A NullPointerException exception thrown at runtime indicates the existence * of an underlying null pointer dereference that must be fixed in the application * code (see EXP01-J. Do not use a null in a case where an object is required for more * information). Handling the underlying null pointer dereference by catching the * NullPointerException rather than fixing the underlying problem is * inappropriate for several reasons. First, catching NullPointerException * adds significantly more performance overhead than simply adding the necessary null * checks [Bloch 2008]. Second, when multiple expressions in a try block are capable of * throwing a NullPointerException, it is difficult or impossible to * determine which expression is responsible for the exception because the * NullPointerException catch block handles any NullPointerException * thrown from any location in the try block. Third, programs rarely remain in an expected * and usable state after a NullPointerException has been thrown. Attempts * to continue execution after first catching and logging (or worse, suppressing) the * exception rarely succeed. *

* @author Ben White (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: ERR08-J */ class ERR08J_DoNotCatchNullPointerException extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

if (node instanceof CatchClause) { // Get the exception type being caught SingleVariableDeclaration exception = ((CatchClause)node).getException(); String exceptionType = exception.resolveBinding().getType().getQualifiedName(); // Is it NullPointerException or one of its ancestors? ruleViolated = exceptionType.equals(NullPointerException.class.getCanonicalName()) || exceptionType.equals(RuntimeException.class.getCanonicalName()) || exceptionType.equals(Exception.class.getCanonicalName()) || exceptionType.equals(Throwable.class.getCanonicalName());

//if node violates rule, check whether method contains skip rule check if (ruleViolated) { ruleViolated = super.violated(node); } }

return ruleViolated; }

@Override public String getRuleText() { return "Catching a null pointer exception (or any of its ancestors) increases " + "performance overhead, are difficult to troubleshoot and programs " + "rarely remain in a usable state after they are thrown."; }

@Override

61

public String getRuleName() { return Globals.RuleNames.ERR08_J; }

@Override public String getRuleRecommendation() { return "Test for null before dereferencing and permit the exception to be " + "thrown."; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_HIGH; }

@Override public TreeMap getSolutions(ASTNode node) { TreeMap list = new TreeMap(); list.putAll(super.getSolutions(node));

try { AST ast = node.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); rewrite.remove(node, null);

list.put("Remove Catch Clause", rewrite); } catch (Exception e) { throw new IllegalArgumentException(e); } return list; }

@Override public String getRuleID() { return Globals.RuleID.ERR08_J; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: EXP00-J. Do not ignore values returned by methods *

*

* CERT Website: Methods can return values to communicate failure or success * or to update local objects or fields. Security risks can arise when method * return values are ignored or when the invoking method fails to take suitable * action. Consequently, programs must not ignore method return values. *

*

* When getter methods are named after an action, a programmer could fail to * realize that a return value is expected. For example, the only purpose of * the ProcessBuilder.redirectErrorStream() method is to report * via return value whether the process builder successfully merged standard * error and standard output. The method that actually performs redirection * of the error stream is the overloaded single-argument method * ProcessBuilder.redirectErrorStream(boolean). *

* @author Ben White (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: EXP00-J

62

*/ class EXP00J_DoNotIgnoreValuesReturnedByMethods extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

// Is the node a method invocation? if (node instanceof MethodInvocation) { MethodInvocation method = (MethodInvocation) node; ITypeBinding returnType = method.resolveMethodBinding() == null ? null : method.resolveMethodBinding().getReturnType(); // Does it have a return type? if (returnType != null && !returnType.getQualifiedName().equals("void")) { ASTNode parent = method.getParent(); // Was the return type used? // (If ExpressionStatement then it was not used ruleViolated = parent instanceof ExpressionStatement; }

//if node violates rule, check whether method contains skip rule check if (ruleViolated) { ruleViolated = super.violated(node); } }

return ruleViolated; }

@Override public String getRuleText() { return "CERT Website-Methods can return values to communicate failure or " + "success or to update local objects or fields. Security risks can " + "arise when method return values are ignored or when the invoking " + "method fails to take suitable action. Consequently, programs must " + "not ignore method return values."; }

@Override public String getRuleName() { return Globals.RuleNames.EXP00_J; }

@Override public String getRuleRecommendation() { return "Capture the return value of the method call"; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_MEDIUM; }

@Override public TreeMap getSolutions(ASTNode node) { TreeMap map = new TreeMap(); map.putAll(super.getSolutions(node));

try { AST ast = node.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast);

63

MethodInvocation methodInvocation = (MethodInvocation) node; Type type = null; if (methodInvocation.resolveMethodBinding().getReturnType().isPrimitive()) { type = ast.newPrimitiveType(

PrimitiveType.toCode(methodInvocation.resolveMethodBinding().getReturnType().getName())); } else { type = ast

.newSimpleType(ast.newName(methodInvocation.resolveMethodBinding().getReturnType().getName ())); }

SingleVariableDeclaration newVariableDeclaration = ast.newSingleVariableDeclaration(); newVariableDeclaration.setName(ast.newSimpleName("returnValue")); newVariableDeclaration.setType(type); newVariableDeclaration.setInitializer((Expression) rewrite.createCopyTarget(methodInvocation));

rewrite.replace(methodInvocation, newVariableDeclaration, null);

map.put("Store return value in variable returnValue", rewrite); } catch (Exception e) { throw new IllegalArgumentException(e); } return map; }

@Override public String getRuleID() { return Globals.RuleID.EXP00_J; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: EXP02-J. Do not use the Object.equals() method * to compare two arrays *

*

* CERT Website: In Java, arrays are objects and support object methods such * as Object.equals(). However, arrays do not support any methods * besides those provided by Object. Consequently, using * Object.equals() on any array compares only array references, not * their contents. Programmers who wish to compare the contents of two arrays * must use the static two-argument Arrays.equals() method. * This method considers two arrays equivalent if both arrays contain the same * number of elements, and all corresponding pairs of elements in the two arrays * are equivalent, according to Object.equals(). In other words, * two arrays are equal if they contain equivalent elements in the same order. * To test for reference equality, use the reference equality operators, == and * !=. *

*

* Because the effect of using Object.equals() to compare two arrays * is often misconstrued as content equality, and because a better alternative * exists in the use of reference equality operators, the use of the * Object.equals() method to compare two arrays is disallowed. *

64

* @author Ben White (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: EXP02-J */ class EXP02J_DoNotUseObjectEqualsToCompareArrays extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

// Is node a method invocation? if (node instanceof MethodInvocation) { MethodInvocation method = (MethodInvocation)node; // Was equals called from an array? if (method.getExpression() != null && method.getExpression().resolveTypeBinding() != null) ruleViolated = method.getExpression().resolveTypeBinding().isArray() && method.getName().getFullyQualifiedName().equals("equals");

//if node violates rule, check whether method contains skip rule check if (ruleViolated) { ruleViolated = super.violated(node); } } return ruleViolated; }

@Override public String getRuleText() { return "CERT Website-In Java, arrays are objects and support object " + "methods such as Object.equals(). However, arrays do not " + "support any methods besides those provided by Object. " + "Consequently, using Object.equals() on any array compares only " + "array references, not their contents."; }

@Override public String getRuleName() { return Globals.RuleNames.EXP02_J; }

@Override public String getRuleRecommendation() { return "CERT Website-Programmers who wish to " + "compare the contents of two arrays must use the static two-" + "argument Arrays.equals() method. This method considers two " + "arrays equivalent if both arrays contain the same number of " + "elements, and all corresponding pairs of elements in the two " + "arrays are equivalent, according to Object.equals(). In other " + "words, two arrays are equal if they contain equivalent elements " + "in the same order. To test for reference equality, use the " + "reference equality operators, == and !=."; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_MEDIUM; }

65

@SuppressWarnings("unchecked") @Override public TreeMap getSolutions(ASTNode node) { // Add solutions to list; TreeMap map = new TreeMap(); map.putAll(super.getSolutions(node)); try { AST ast = node.getAST();

MethodInvocation methodInvocation = (MethodInvocation) node;

// Solution 1: Arrays.equals(array1, array2); ASTRewrite rewrite1 = ASTRewrite.create(ast); MethodInvocation newMethodInvocation1 = ast.newMethodInvocation(); newMethodInvocation1.setExpression(ast.newName("Arrays")); newMethodInvocation1.setName(ast.newSimpleName("equals"));

newMethodInvocation1.arguments() .add((Expression) rewrite1.createCopyTarget(methodInvocation.getExpression())); newMethodInvocation1.arguments() .add((Expression) rewrite1.createCopyTarget((ASTNode) methodInvocation.arguments().get(0)));

rewrite1.replace(methodInvocation, newMethodInvocation1, null);

// Solution 2: array1 == array2; ASTRewrite rewrite2 = ASTRewrite.create(ast); InfixExpression infixExpression = ast.newInfixExpression(); infixExpression.setLeftOperand((Expression) rewrite2.createCopyTarget(methodInvocation.getExpression())); infixExpression.setOperator(InfixExpression.Operator.EQUALS);

infixExpression.setRightOperand(ast.newSimpleName(methodInvocation.arguments().get(0).toSt ring()));

rewrite2.replace(methodInvocation, infixExpression, null);

map.put("Use Arrays.equals() to compare", rewrite1); map.put("Use == to compare", rewrite2);

} catch (Exception e) { throw new IllegalArgumentException(e); } return map; }

@Override public String getRuleID() { return Globals.RuleID.EXP02_J; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: FIO08-J. Distinguish between characters or bytes read from a * stream and -1 *

*

* CERT Website: The abstract InputStream.read() and Reader.read() * methods are used to read a byte or character, respectively, from a stream. The * InputStream.read() method reads a single byte from an input source and

66

* returns its value as an int in the range 0 to 255 (0x00-0xff). The * Reader.read() method reads a single character and returns its value as an * int in the range 0 to 65,535 (0x0000-0xffff). Both methods return the * 32-bit value -1 (0xffffffff) to indicate that the end of the stream has been reached * and no data is available. The larger int size is used by both methods to * differentiate between the end-of-stream indicator and the maximum byte (0xff) or * character (0xffff) value. The end-of-stream indicator is an example of an in-band * error indicator. In-band error indicators are problematic to work with, and the * creation of new in-band-error indicators is discouraged. *

*

* Prematurely converting the resulting int to a byte or * char before testing for the value -1 makes it impossible to distinguish * between characters read and the end of stream indicator. Programs must check for the * end of stream before narrowing the return value to a byte or * char. *

*

* This rule applies to any method that returns the value -1 to indicate the end of a * stream. It includes any InputStream or Reader subclass * that provides an implementation of the read() method. This rule is a * specific instance of NUM12-J. Ensure conversions of numeric types to narrower types * do not result in lost or misinterpreted data. *

* @author (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: FIO08-J */ class FIO08J_DistinguishBetweenCharactersOrBytes extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false;

// Check to see if in CastExpression if (node instanceof CastExpression) { CastExpression cast = (CastExpression)node; // Check to see if expression portion is a method invocation if (cast.getExpression() instanceof MethodInvocation) { MethodInvocation method = (MethodInvocation)cast.getExpression(); String castType = cast.getType().resolveBinding().getQualifiedName(); // Rule is violated if InputStream.read() or ancestor is called and cast to byte // or if Reader.read() or ancestor is called and cast to char ruleViolated = (castType.equals(byte.class.getCanonicalName()) && Utility.calledMethod(method, InputStream.class.getCanonicalName(), "read", true)) || (castType.equals(char.class.getCanonicalName()) && Utility.calledMethod(method, Reader.class.getCanonicalName(), "read", true)); } if (ruleViolated) ruleViolated = super.violated(node); }

return ruleViolated; }

@Override public String getRuleText() { return "The result of InputStream.read() or Reader.read() must never be cast "

67

+ "to a byte or a character since the return type is an integer and -1 " + "(0xffffffff) is used to indicate end of stream. If InputStream.read() " + "returns 0xff or Reader.read returns 0xffff then casting to an int " + "will make it impossible to distinguish between end of streem and the " + "next byte or character."; }

@Override public String getRuleName() { return Globals.RuleNames.FIO08_J; }

@Override public String getRuleRecommendation() { return "Do not cast the result of InputStream.read() to a byte or Reader.read() " + "to a byte."; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_HIGH; }

@Override public String getRuleID() { return Globals.RuleID.FIO08_J; }

@Override public TreeMap getSolutions(ASTNode node) { TreeMap map = new TreeMap<>(); map.putAll(super.getSolutions(node)); try { // Change "data = (cast)InputStream.read() or (cast)Reader.read()" to "InputStream.read() or Reader.read()" AST ast = node.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast);

CastExpression caseExpression = (CastExpression)node; MethodInvocation oldMethodInvocation = (MethodInvocation)caseExpression.getExpression(); MethodInvocation newMethodInvocation = (MethodInvocation) rewrite.createCopyTarget(oldMethodInvocation); rewrite.replace(caseExpression, newMethodInvocation, null);

map.put("Remove cast", rewrite); } catch (Exception e) { throw new IllegalArgumentException(e); } return map; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: IDS00-J. Prevent SQL injection *

*

68

* CERT Website: SQL injection vulnerabilities arise in applications where elements of a SQL query originate * from an untrusted source. Without precautions, the untrusted data may maliciously alter * the query, resulting in information leaks or data modification. The primary means of preventing * SQL injection are sanitization and validation, which are typically implemented as parameterized * queries and stored procedures. *

* @author (Rule Violation Plugin Logic), CERT (Rule Definition), Chen Li (Rule Violation Solution Plugin Logic) * @see Java Secure Coding Rule defined by CERT: IDS00-J */ class IDS00J_PreventSQLInjection extends SecureCodingRule {

@Override public boolean violated(ASTNode node) { boolean ruleViolated = false; MethodInvocation method;

// Detect use of Statement.ExecuteQuery or PreparedStatement.ExecuteQuery if (node instanceof MethodInvocation) { method = (MethodInvocation) node; // If PreparedStatement was used then make sure that setString() was // called at least once, if not then the rule is violated if (Utility.calledMethod(method, PreparedStatement.class.getCanonicalName(), "executeQuery")) ruleViolated = !Utility.calledPrior(method, PreparedStatement.class.getCanonicalName(), "setString"); // If PreparedStatement was not used then see if Statement was used // and then return true if the string being used wasn't a literal string else if (Utility.calledMethod(method, Statement.class.getCanonicalName(), "executeQuery")) { // Rule is violated if the argument was created using a compound expression // (anything except for a literal string) // In this first case there is a variable being passed to executeQuery() if (method.arguments().size() > 0 && method.arguments().get(0) instanceof SimpleName) { // The argument in this case would be the query string being passed to the database SimpleName argument = (SimpleName) method.arguments().get(0); ASTNode parent = node.getParent();

// Look for assignments to the query string in parent nodes while (parent != null && !ruleViolated) { ASTNodeProcessor processor = new ASTNodeProcessor(); parent.accept(processor); // Check all of the assignments for(NodeNumPair assignmentPair : processor.getAssignments()) { Assignment assignment = (Assignment)assignmentPair.getNode(); // If the assignment is for the query string and the right hand side // is not a literal string then the rule has been violated if (assignment.getLeftHandSide().subtreeMatch(new ASTMatcher(), argument)

69

&& !(assignment.getRightHandSide() instanceof StringLiteral)) { ruleViolated = true; break; } } // If the rule still isn't violated, check all of the declarations if (!ruleViolated) { for(NodeNumPair declarationPair : processor.getVariableDeclarationFragments()) { VariableDeclarationFragment declaration = (VariableDeclarationFragment) declarationPair.getNode(); // If the variable being declared is the query string and the right hand // side is not a literal string then the rule has been violated if (declaration.getName().subtreeMatch(new ASTMatcher(), argument)

&& !(declaration.getInitializer() instanceof StringLiteral)) { ruleViolated = true; break; } } } parent = parent.getParent(); } } // In this case there is another type of argument being sent, the rule // is violated if that argument is anything except a string literal else // In this case the argument could have been a simple name ruleViolated = !(method.arguments().size() > 0 && method.arguments().get(0) instanceof StringLiteral); } if (ruleViolated) ruleViolated = super.violated(node); }

return ruleViolated; }

@Override public String getRuleText() { return "CERT Website-SQL injection vulnerabilities arise in applications where elements of a SQL query " + "originate from an untrusted source. Without precautions, the untrusted data may " + "maliciously alter the query, resulting in information leaks or data modification. " + "The primary means of preventing SQL injection are sanitization and validation, " + "which are typically implemented as parameterized queries and stored procedures."; }

@Override public String getRuleName() { return Globals.RuleNames.IDS00_J; }

70

@Override public String getRuleRecommendation() { return "Do not execute SQL queries with parameters directly, use a PreparedStatement and the " + "setString() method to insert the parameters"; }

@Override public int securityLevel() { return Globals.Markers.SECURITY_LEVEL_HIGH; }

@Override public String getRuleID() { return Globals.RuleID.IDS00_J; }

@SuppressWarnings({ "unchecked" }) @Override public TreeMap getSolutions(ASTNode node) {

TreeMap list = new TreeMap(); list.putAll(super.getSolutions(node));

try { // stmt.executeQuery(sqlString or null) MethodInvocation method = (MethodInvocation) node; AST ast = node.getAST();

// if methodInvocation is stmt.executeQuery(sqlString) get variable // name and argument that called the executeQuery if ("executeQuery".equals(method.getName().getIdentifier()) && Statement.class.getCanonicalName()

.equals(method.getExpression().resolveTypeBinding().getQualifiedName())) {

ASTRewrite rewrite1 = ASTRewrite.create(ast);

// get stmt: stmt.executeQuery(sqlString) String stmt = method.getExpression().toString();

// set the arguments of stmt.executeQuery(sqlString) to null; MethodInvocation newStmtMethodInvocation = (MethodInvocation) ast.newMethodInvocation();

newStmtMethodInvocation.setName(ast.newSimpleName("executeQuery")); newStmtMethodInvocation.setExpression(ast.newSimpleName(stmt)); rewrite1.replace(method, newStmtMethodInvocation, null);

// get sqlString: stmt.executeQuery(sqlString) String sqlString = method.arguments().get(0).toString();

ASTNode astBlock = Utility.getEnclosingNode(method, Block.class); if (astBlock != null && astBlock instanceof Block) { Block block = (Block) astBlock; SecureCodingNodeVisitor visitor = new SecureCodingNodeVisitor(); block.accept(visitor);

// Change Statement stmt = connection.createStatement() to // PreparedStatement stmt = // connection.prepareStatement(sqlString); Type oldType = visitor.getType(stmt);

71

Type newType = ast.newSimpleType(ast.newName("PreparedStatement")); if (oldType != null) rewrite1.replace(oldType, newType, null);

VariableDeclarationFragment stmtADF = visitor.getVariableDeclarationFragment(stmt); if (stmtADF != null && stmtADF.getInitializer() != null) { if (stmtADF.getInitializer() instanceof MethodInvocation) { MethodInvocation mi = (MethodInvocation) stmtADF.getInitializer(); MethodInvocation stmtMI = ast.newMethodInvocation();

stmtMI.setName(ast.newSimpleName("prepareStatement"));

stmtMI.setExpression(ast.newSimpleName(mi.getExpression().toString()));

stmtMI.arguments().add(ast.newSimpleName(sqlString)); rewrite1.replace(mi, stmtMI, null); } } else { ArrayList stmtMIs = visitor.getMethodInvocations("createStatement",

Connection.class.getCanonicalName(), 0, new ArrayList<>()); MethodInvocation stmtMI = ast.newMethodInvocation();

stmtMI.setName(ast.newSimpleName("prepareStatement"));

stmtMI.arguments().add(ast.newSimpleName(sqlString)); for (MethodInvocation mi : stmtMIs) { if (stmt.equals(visitor.getVariable(mi))) {

stmtMI.setExpression(ast.newSimpleName(mi.getExpression().toString())); rewrite1.replace(mi, stmtMI, null); break; } } }

} list.put("Using PreparedStatement instead of Statement", rewrite1);

} else { ASTRewrite rewrite2 = ASTRewrite.create(ast);

// methodInvocation: stmt.executeQuery(); org.eclipse.jdt.core.dom.Statement oldStmt = SecureCodingNodeVisitor.getStatement(method); String stmt = method.getExpression().toString();

// insert setString(1, username); setString(2, password) before // stmt.executeQuery(); MethodInvocation setString1 = ast.newMethodInvocation(); setString1.arguments().add(ast.newNumberLiteral("1")); setString1.arguments().add(ast.newSimpleName("username")); setString1.setName(ast.newSimpleName("setString")); setString1.setExpression(ast.newSimpleName(stmt));

72

ExpressionStatement expstmt1 = ast.newExpressionStatement(setString1);

MethodInvocation setString2 = ast.newMethodInvocation(); setString2.arguments().add(ast.newNumberLiteral("2")); setString2.arguments().add(ast.newSimpleName("password")); setString2.setName(ast.newSimpleName("setString")); setString2.setExpression(ast.newSimpleName(stmt)); ExpressionStatement expstmt2 = ast.newExpressionStatement(setString2);

ASTNode astBlock = Utility.getEnclosingNode(method, Block.class); if (astBlock != null && astBlock instanceof Block) { Block block = (Block) astBlock; ListRewrite listRewrite = rewrite2.getListRewrite(block, Block.STATEMENTS_PROPERTY); listRewrite.insertBefore(expstmt1, oldStmt, null); listRewrite.insertBefore(expstmt2, oldStmt, null);

// Redefine String sqlString = "select * from db_user where // username=? and password=?"; SecureCodingNodeVisitor visitor = new SecureCodingNodeVisitor(); block.accept(visitor);

// stmt = connection.prepareStatement(sqlString); ArrayList arguTypes = new ArrayList<>(); arguTypes.add(String.class.getCanonicalName()); ArrayList MIs = visitor.getMethodInvocations("prepareStatement", Connection.class.getCanonicalName(), 1, arguTypes); for (MethodInvocation mi : MIs) { if (stmt.equals(visitor.getVariable(mi))) { String sqlString = mi.arguments().get(0).toString();

VariableDeclarationFragment sqlStringVDF = visitor

.getVariableDeclarationFragment(sqlString); StringLiteral sl = ast.newStringLiteral(); sl.setLiteralValue("select * from db_user where username=? and password=?");

if (sqlStringVDF != null && sqlStringVDF.getInitializer() != null) {

rewrite2.replace(sqlStringVDF.getInitializer(), sl, null); } else { Assignment sqlStringAssign = ast.newAssignment();

sqlStringAssign.setLeftHandSide(ast.newSimpleName(sqlString));

sqlStringAssign.setOperator(Operator.ASSIGN);

sqlStringAssign.setRightHandSide(sl); ExpressionStatement expStmt = ast.newExpressionStatement(sqlStringAssign); ListRewrite listRewrite2 = rewrite2.getListRewrite(block, Block.STATEMENTS_PROPERTY);

73

listRewrite2.insertBefore(expStmt, SecureCodingNodeVisitor.getStatement(mi), null); } break; } } } list.put("Using setString() to pass in user input", rewrite2); } } catch (Exception e) { throw new IllegalArgumentException(e); } return list; } }

/** * The text and/or code below is from the CERT website: https://www.securecoding.cert.org *

* Java Secure Coding Rule: IDS01-J. Normalize strings before validating * them *

*

* CERT Website: Many applications that accept untrusted input strings employ input filtering * and validation mechanisms based on the strings' character data. For example, * an application's strategy for avoiding cross-site scripting (XSS) * vulnerabilities may include forbidding {@code