Example: Synthetic method creation

This example demonstrates the creation of a synthetic method. In this synthetic method, we first taint a class field and then add a call to a method in the user's code and finally pass the result of this method call to a sink.

Application scenario

In this example, createUser is a REST API implemented in the JAX-RS framework. A user can invoke this API using a URL such as http://host:port/users/createUser. The framework runtime delegates this call to UserRestService.createUser() since the path matches. Additionally, the framework runtime initializes the urlInfo class variable from the client data before calling createUser(). Finally, the data returned by createUser() is sent back to the client by the runtime.

What is modelled in the synthetic method

To capture the Application scenario using a synthetic method, we have these elements:

  • The tainting of the urlInfo class variable of UserRestService.
  • The call to UserRestService.createUser().
  • The return of data to the client.

This is the code for the createUser() method:

import java.io.BufferedWriter;

@Path("users")
public class UserRestService {
  @Context
  UriInfo urlInfo;

  @Path("/createUser")
  public User createUser(){
    MultivaluedMap<String, String> queryParams = 
      urlInfo.getQueryParameters();
    String name = queryParams.getFirst("name");
    User user = new User(name);
    return user;
  }
}

Step 1: Create an empty synthetic method

22    public class JAXRSHandler extends F4FHandler{
23    @Override
        public void handleApp(F4FApp app, F4FActions actions) {
24    HighLevelSyntheticMethod synthMethod = HighLevelSyntheticMethod.make();           
  • The synthetic method object is initialized at line 24.

Step 2: Model the tainting of a class field

The code snippet below demonstrates how specific class fields can be tainted. In this example, we wish to taint the class fields that are annotated with the @Context annotation.

27    // create a local variable of the appropriate type 
28    Local userRestServiceClazzInstance = 
        synthMethod.newLocal("com.ibm.appscan.UserRestService");
29
30    // get all the class fields
31    for(IField field: app.getIClass
        ("com.ibm.appscan.UserRestService").getDeclaredInstanceFields()){
32
33      //get all the annotations associated with the field   
34      Collection<Annotation> fieldAnnotations = app.getFieldAnnotations(field);
35
36      //for each annotation of the field check if it is an @Context annotation 
37      for (Annotation annotation : fieldAnnotations) {
38
39        if (annotation.getType().getName().toString().equals
            ("Ljavax/ws/rs/core/Context")) {
40
41          // call the F4F API to assign taint to the field
43          synthMethod.addInstanceVariableWrite(
                 userRestServiceClazzInstance /*Variable representing 
                 class instance*/,
44               field /*field to taint  */, 
                 Taint.taint() /* taint */, 
                 null);
45        }
46      }
47    }

The Framework for Frameworks API addInstanceVariableWrite takes four arguments:

  1. The first argument is the class reference whose field we want to taint. In the example, the local variable userRestServiceClazzInstance refers to this argument.
  2. The second argument is the class field we want to taint.
  3. The third argument is the new value that will be assigned to the class variable field. In the example, we want to taint this variable so that a Taint.taint() is passed.
  4. The last argument is FilePositionInfo, which is null in this example.

Step 3: Model the calling of the createUser() method

In this step, we simulate the call in our synthetic method.

50    // call createUser() and store the returned value to a local variable
51    Local returnObj = synthMethod.addCall(
52      "com.ibm.appscan.UserRestService.createUser():com.ibm.appscan.User"
          /* signature of the method to be called */,
          null, /* FilePositionInfo */
53        userRestServiceClazzInstance /* Object on which the method 
            will be called */);

The addCall high-level API takes three arguments:

  • The first argument is the signature of the method that we want to invoke. In this case, we want to invoke User.createUser(), hence its signature is used.
  • The second argument is FilePositionInfo. It is passed as null as it is not required for this example.
  • The last argument represents the list of parameters which are required to invoke the createUser() method.

Since createUser() takes no arguments, the only argument passed to this call is the this object, a Local variable (userRestServiceClazzInstance) of the type User. The method createUser() returns a newly-created User which is stored in a new Local variable called returnedObj (as shown on line 51).

Step 4: Model data return to the client

In this final step, we create a sink to model the return of data back to the client.

58    // create a PrintWriter object
59    Local printWriterObj = synthMethod.newLocal("java.io.PrintWriter");
60
61    // we want to call the print API on the PrintWriter object. 
      // The print API takes a single argument of Object type returns void
62
63    // we create a param list of size 2. The first item in this list is always the 
      //  'this' object and rest are actual method arguments. 
64    Param[] printWriterObjParam =  new Param[2];
65    printWriterObjParam[0] = printWriterObj;  // The "this" object
      // the value returned by the call to the createUser method
66    printWriterObjParam[1] = returnObj;
67
68    // Now add a call to the print method
69    synthMethod.addCall("java.io.PrintWriter.print(java.lang.Object):void", 
        null, printWriterObjParam);                      
70  }
  • At line 59, we create a Local object for the PrintWriter class in the synthetic method.
  • From lines 64 to 66, we prepare the parameter list which is required to invoke the print method on the PrintWriter class instance (printWriterObj):
    • The first parameter is the object reference (printWriterObj), itself.
    • The second parameter is the argument to the print method. We want to pass the return value which is stored in the returnObj Local variable.
  • At line 69, we add a call to the PrintWriter.print method to model the return of data back to the client.